PHP_Archive
[ class tree: PHP_Archive ] [ index: PHP_Archive ] [ all elements ]

Source for file Manager.php

Documentation is available at Manager.php

  1. <?php
  2. /**
  3.  * PHP_Archive Manager Class creator (allows debugging/manipulation of phar files)
  4.  *
  5.  * @package PHP_Archive
  6.  * @category PHP
  7.  */
  8. /**
  9.  * Needed for file manipulation
  10.  */
  11. require_once 'System.php';
  12. require_once 'PHP/Archive/Exception.php';
  13.  
  14.  
  15. /**
  16.  *
  17.  * @copyright Copyright ? Gregory Beaver
  18.  * @author Greg Beaver <cellog@php.net>
  19.  * @version $Id: Manager.php,v 1.14 2007/08/18 03:57:34 cellog Exp $
  20.  * @package PHP_Archive
  21.  * @category PHP
  22.  */
  23. {
  24.     const GZ = 0x00001000;
  25.     const BZ2 = 0x00002000;
  26.     const SIG = 0x00010000;
  27.     const SHA1 = 0x0002;
  28.     const MD5 = 0x0001;
  29.     private $_alias;
  30.     private $_archiveName;
  31.     private $_apiVersion;
  32.     private $_flags;
  33.     private $_knownAPIVersions = array('1.0.0');
  34.     private $_manifest;
  35.     private $_fileStart;
  36.     private $_manifestSize;
  37.     private $_html;
  38.     private $_metadata;
  39.     private $_sigtype;
  40.     private $_signature = false;
  41.     /**
  42.      * Locate the .phar archive in the include_path and detect the file to open within
  43.      * the archive.
  44.      *
  45.      * Possible parameters are phar://filename_within_phar.ext or
  46.      * phar://pharname.phar/filename_within_phar.ext
  47.      *
  48.      * phar://filename_within_phar.ext will simply use the last .phar opened.
  49.      * @param string a file within the archive
  50.      * @return string the filename within the .phar to retrieve
  51.      */
  52.     public function __construct($phar)
  53.     {
  54.         $this->_archiveName $phar;
  55.         $this->validate();
  56.     }
  57.  
  58.     /**
  59.      * validate a phar prior to manipulating it
  60.      * @throws PHP_Archive_Exception
  61.      */
  62.     public function validate($strict = false)
  63.     {
  64.         $errors = array();
  65.         $warnings = array();
  66.         $fp fopen($this->_archiveName'rb');
  67.         if (!$fp{
  68.             throw new PHP_Archive_ExceptionExtended(PHP_Archive_ExceptionExtended::NOOPEN,
  69.                 array('archive' => $this->_archiveName));
  70.         }
  71.         $header fread($fpstrlen('<?php #PHP_ARCHIVE_HEADER-'));
  72.         if ($header == '<?php #PHP_ARCHIVE_HEADER-'{
  73.             $version '';
  74.             while (!feof($fp&& (false !== $c fgetc($fp))) {
  75.                 if ((ord($cord('0'|| ord($cord('9')) && $c != '.'{
  76.                     break;
  77.                 }
  78.                 $version .= $c;
  79.             }
  80.             if (version_compare($version'0.8.0''<')) {
  81.                 throw new PHP_Archive_Exception($phar ' was created with obsolete PHP_Archive',
  82.                     $errors);
  83.             }
  84.             $this->_version $version;
  85.         }
  86.         // seek to __HALT_COMPILER_OFFSET__
  87.         $found = false;
  88.         while (!feof($fp&& false != ($next fread($fp8192))) {
  89.             if (false != ($t strpos($next'__HALT_COMPILER();'))) {
  90.                 fseek($fp$t strlen($nextstrlen('__HALT_COMPILER();')SEEK_CUR);
  91.                 $found = true;
  92.                 break;
  93.             }
  94.         }
  95.         if (!$found{
  96.             throw new PHP_Archive_ExceptionExtended(PHP_Archive_ExceptionExtended::NOTPHAR,
  97.                 array('archive' => $this->_archiveName));
  98.         }
  99.         $manifest_length fread($fp4);
  100.         $manifest_length unpack('Vlen'$manifest_length);
  101.         $this->_manifestSize $manifest_length $manifest_length['len'];
  102.         if ($manifest_length > 1048576{
  103.             if ($strict{
  104.                 throw new PHP_Archive_ExceptionExtended(
  105.                     PHP_Archive_ExceptionExtended::MANIFESTOVERFLOWarray(
  106.                     'archive' => $this->_archiveName));
  107.             }
  108.             $errors[= new PHP_Archive_ExceptionExtended(
  109.                 PHP_Archive_ExceptionExtended::MANIFESTOVERFLOWarray(
  110.                 'archive' => $this->_archiveName));
  111.         }
  112.         $manifest fread($fp$manifest_length);
  113.         // retrieve the number of files in the manifest
  114.         $info unpack('V'substr($manifest04));
  115.         if ($info[1* 24 > $manifest_length{
  116.             $errors[= new PHP_Archive_ExceptionExtended(
  117.                 PHP_Archive_ExceptionExtended::MANIFESTENTRIESOVERFLOW,array(
  118.                 'archive' => $this->_archiveName));
  119.             throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  120.         }
  121.         $manifest substr($manifest4);
  122.         if (strlen($manifest< 4{
  123.             $errors[= new PHP_Archive_ExceptionExtended(
  124.                 PHP_Archive_ExceptionExtended::MANIFESTENTRIESUNDERFLOWarray(
  125.                 'archive' => $this->_archiveName));
  126.             throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  127.         }
  128.         // get API version and compressed flag
  129.         $apiver substr($manifest02);
  130.         $apiver bin2hex($apiver);
  131.         $this->_apiVersion hexdec($apiver[0]'.' hexdec($apiver[1].
  132.             '.' hexdec($apiver[2]);
  133.         if (!in_array($this->_apiVersion$this->_knownAPIVersions)) {
  134.             $errors[= new PHP_Archive_ExceptionExtended(
  135.                 PHP_Archive_ExceptionExtended::UNKNOWNAPIarray(
  136.                 'archive' => $this->_archiveName'ver' => $this->_apiVersion));
  137.             throw new PHP_Archive_Exception('phar "' $this->_archiveName '" cannot be analyzed'$errors);
  138.         }
  139.         $manifest substr($manifest2);
  140.         if (strlen($manifest< 4{
  141.             $errors[= new PHP_Archive_ExceptionExtended(
  142.                 PHP_Archive_ExceptionExtended::MANIFESTENTRIESUNDERFLOWarray(
  143.                 'archive' => $this->_archiveName));
  144.             throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  145.         }
  146.         // get flags
  147.         $flags unpack('V'substr($manifest04));
  148.         $this->_flags $flags[1];
  149.         $manifest substr($manifest4);
  150.         if (strlen($manifest< 4{
  151.             $errors[= new PHP_Archive_ExceptionExtended(
  152.                 PHP_Archive_ExceptionExtended::MANIFESTENTRIESUNDERFLOWarray(
  153.                 'archive' => $this->_archiveName));
  154.             throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  155.         }
  156.         // get alias
  157.         $aliaslen unpack('V'substr($manifest04));
  158.         $aliaslen $aliaslen[1];
  159.         $manifest substr($manifest4);
  160.         if (strlen($manifest$aliaslen{
  161.             $errors[= new PHP_Archive_ExceptionExtended(
  162.                 PHP_Archive_ExceptionExtended::MANIFESTENTRIESUNDERFLOWarray(
  163.                 'archive' => $this->_archiveName));
  164.             throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  165.         }
  166.         $this->_alias substr($manifest0$aliaslen);
  167.         $manifest substr($manifest$aliaslen);
  168.         // phar metadata
  169.         if (strlen($manifest< 4{
  170.             $errors[= new PHP_Archive_ExceptionExtended(
  171.                 PHP_Archive_ExceptionExtended::MANIFESTENTRIESUNDERFLOWarray(
  172.                 'archive' => $this->_archiveName));
  173.             throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  174.         }
  175.         $metadatalen unpack('V'substr($manifest04));
  176.         $metadatalen $metadatalen[1];
  177.         $manifest substr($manifest4);
  178.         if ($metadatalen{
  179.             if (strlen($manifest$metadatalen{
  180.                 $errors[= new PHP_Archive_ExceptionExtended(
  181.                     PHP_Archive_ExceptionExtended::MANIFESTENTRIESUNDERFLOWarray(
  182.                     'archive' => $this->_archiveName));
  183.                 throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  184.             }
  185.             $this->_metadata unserialize(substr($manifest0$metadatalen));
  186.             $manifest substr($manifest$metadatalen);
  187.         }
  188.         $ret = array();
  189.         $offset = 0;
  190.         for ($i = 0; $i $info[1]$i++{
  191.             if (strlen($manifest< 4{
  192.                 if (isset($savepath)) {
  193.                     $errors[= new PHP_Archive_ExceptionExtended(
  194.                         PHP_Archive_ExceptionExtended::MANIFESTENTRIESTRUNCATEDENTRYarray(
  195.                         'archive' => $this->_archiveName'last' => $savepath,
  196.                         'current' => '*unknown*''size' => $info[1]'cur' => $i));
  197.                     throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  198.                 else {
  199.                     $errors[= new PHP_Archive_ExceptionExtended(
  200.                         PHP_Archive_ExceptionExtended::MANIFESTENTRIESTRUNCATEDENTRYarray(
  201.                         'archive' => $this->_archiveName'last' => '*none*',
  202.                         'current' => '*unknown*''size' => $info[1]'cur' => $i));
  203.                     throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  204.                 }
  205.             }
  206.             // length of the file name
  207.             $len unpack('V'substr($manifest04));
  208.             if (strlen($manifest$len[1+ 4{
  209.                 if (isset($savepath)) {
  210.                     $errors[= new PHP_Archive_ExceptionExtended(
  211.                         PHP_Archive_ExceptionExtended::MANIFESTENTRIESTRUNCATEDENTRYarray(
  212.                         'archive' => $this->_archiveName'last' => $savepath,
  213.                         'current' => '*unknown*''size' => $info[1]'cur' => $i));
  214.                     throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  215.                 else {
  216.                     $errors[= new PHP_Archive_ExceptionExtended(
  217.                         PHP_Archive_ExceptionExtended::MANIFESTENTRIESTRUNCATEDENTRYarray(
  218.                         'archive' => $this->_archiveName'last' => '*none*',
  219.                         'current' => '*unknown*''size' => $info[1]'cur' => $i));
  220.                     throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  221.                 }
  222.             }
  223.             // file name
  224.             if (!isset($savepath)) {
  225.                 $last '*none*';
  226.             else {
  227.                 $last $savepath;
  228.             }
  229.             $savepath substr($manifest4$len[1]);
  230.             $manifest substr($manifest$len[1+ 4);
  231.             if (strlen($manifest< 24{
  232.                 if (isset($savepath)) {
  233.                     $errors[= new PHP_Archive_ExceptionExtended(
  234.                         PHP_Archive_ExceptionExtended::MANIFESTENTRIESTRUNCATEDENTRYarray(
  235.                         'archive' => $this->_archiveName'last' => $last,
  236.                         'current' => $savepath'size' => $info[1]'cur' => $i));
  237.                     throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  238.                 else {
  239.                     $errors[= new PHP_Archive_ExceptionExtended(
  240.                         PHP_Archive_ExceptionExtended::MANIFESTENTRIESTRUNCATEDENTRYarray(
  241.                         'archive' => $this->_archiveName'last' => $last,
  242.                         'current' => $savepath'size' => $info[1]'cur' => $i));
  243.                     throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  244.                 }
  245.             }
  246.             // retrieve manifest data:
  247.             // 0 = uncompressed file size
  248.             // 1 = save timestamp
  249.             // 2 = compressed file size
  250.             // 3 = crc32
  251.             // 4 = flags
  252.             // 5 = metadata length
  253.             $ret[$savepatharray_values(unpack('Va/Vb/Vc/Vd/Ve/Vf'substr($manifest024)));
  254.             $ret[$savepath][3sprintf('%u'$ret[$savepath][3]
  255.                 0xffffffff);
  256.             $manifest substr($manifest24);
  257.             if ($ret[$savepath][5]{
  258.                 if (strlen($manifest$ret[$savepath][5]{
  259.                     $errors[= new PHP_Archive_ExceptionExtended(
  260.                         PHP_Archive_ExceptionExtended::MANIFESTENTRIESTRUNCATEDENTRY,
  261.                         array('archive' => $this->_archiveName'last' => $last,
  262.                         'current' => $savepath'size' => $info[1]'cur' => $i));
  263.                     throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName .
  264.                         '"'$errors);
  265.                 }
  266.                 $ret[$savepath][6unserialize(fread($fp$ret[$savepath][5]));
  267.             }
  268.             $ret[$savepath][7$offset;
  269.             $offset += $ret[$savepath][2];
  270.         }
  271.         $this->_manifest =  $ret;
  272.         $this->_fileStart ftell($fp);
  273.         foreach ($this->_manifest as $path => $info{
  274.             $currentFilename $path;
  275.             $internalFileLength $info[2];
  276.             // seek to offset of file header within the .phar
  277.             if (fseek($fp$this->_fileStart $info[7])) {
  278.                 $errors[= new PHP_Archive_ExceptionExtended(
  279.                     PHP_Archive_ExceptionExtended::FILELOCATIONINVALID,
  280.                     array('archive' => $this->_archiveName'file' => $path'loc' => $this->_fileStart $info[7],
  281.                     'size' => filesize($this->_archiveName)));
  282.                 continue;
  283.             }
  284.             $temp = array('crc32' => $info[3]'isize' => $info[0]);
  285.             $data '';
  286.             $count $internalFileLength;
  287.             while ($count{
  288.                 if ($count < 8192{
  289.                     $data .= @fread($fp$count);
  290.                     $count = 0;
  291.                 else {
  292.                     $count -= 8192;
  293.                     $data .= @fread($fp8192);
  294.                 }
  295.             }
  296.             if ($info[4self::GZ{
  297.                 $data @gzinflate($data);
  298.                 if ($data === false{
  299.                     $errors[= new PHP_Archive_ExceptionExtended(
  300.                         PHP_Archive_ExceptionExtended::FILECORRUPTEDGZ,
  301.                         array('archive' => $this->_archiveName'file' => $path'loc' => $this->_fileStart $info[2]));
  302.                 }
  303.             }
  304.             if ($info[4self::BZ2{
  305.                 $data @bzdecompress($datatrue);
  306.                 if ($data === false{
  307.                     $errors[= new PHP_Archive_ExceptionExtended(
  308.                         PHP_Archive_ExceptionExtended::FILECORRUPTEDBZ2,
  309.                         array('archive' => $this->_archiveName'file' => $path'loc' => $this->_fileStart $info[2]));
  310.                 }
  311.             }
  312.             if ($temp['isize'!= strlen($data)) {
  313.                 $errors[= new PHP_Archive_ExceptionExtended(
  314.                     PHP_Archive_ExceptionExtended::FILECORRUPTEDSIZE,
  315.                     array('archive' => $this->_archiveName'file' => $path'expected' => $temp['isize'],
  316.                         'actual' => strlen($data)));
  317.             }
  318.             if ($temp['crc32'!= sprintf("%u"crc32($data0xffffffff)) {
  319.                 $errors[= new PHP_Archive_ExceptionExtended(
  320.                     PHP_Archive_ExceptionExtended::FILECORRUPTEDCRC,
  321.                     array('archive' => $this->_archiveName'file' => $path'expected' => $temp['crc32'],
  322.                         'actual' => crc32($data)));
  323.             }
  324.         }
  325.         if ($this->_flags self::SIG{
  326.             do {
  327.                 $end ftell($fp);
  328.                 $data fread($fp28);
  329.                 if (substr($datastrlen($data- 4!= 'GBMB'{
  330.                     $errors[= new PHP_Archive_ExceptionExtended(
  331.                         PHP_Archive_ExceptionExtended::NOSIGNATUREMAGIC,
  332.                         array('archive' => $this->_archiveName));
  333.                     break;
  334.                 }
  335.                 $type unpack('V'substr($datastrlen($data- 84));
  336.                 $all file_get_contents($this->_archiveName);
  337.                 switch ($type[1]{
  338.                     case self::MD5 :
  339.                         $hash substr($allstrlen($all- 16 - 816);
  340.                         $all substr($all0strlen($all- 16 - 8);
  341.                         if (md5(substr($all0$end)true!= $hash{
  342.                             $errors[= new PHP_Archive_ExceptionExtended(
  343.                                 PHP_Archive_ExceptionExtended::BADSIGNATURE,
  344.                                 array('archive' => $this->_archiveName));
  345.                         else {
  346.                             $this->_sigtype 'MD5';
  347.                             $this->_signature md5($all);
  348.                         }
  349.                         break;
  350.                     case self::SHA1 :
  351.                         $hash substr($allstrlen($all- 20 - 820);
  352.                         if (sha1(substr($all0$end)true!= $hash{
  353.                             $errors[= new PHP_Archive_ExceptionExtended(
  354.                                 PHP_Archive_ExceptionExtended::BADSIGNATURE,
  355.                                 array('archive' => $this->_archiveName));
  356.                         else {
  357.                             $this->_sigtype 'SHA1';
  358.                             $this->_signature sha1($all);
  359.                         }
  360.                         break;
  361.                     default :
  362.                         $errors[= new PHP_Archive_ExceptionExtended(
  363.                             PHP_Archive_ExceptionExtended::UNKNOWNSIGTYPE,
  364.                             array('archive' => $this->_archiveName,
  365.                                 'type' => bin2hex(substr($datastrlen($data- 84))));
  366.                         break;
  367.                 }
  368.             while (false);
  369.         }
  370.         @fclose($fp);
  371.         if (count($errors)) {
  372.             throw new PHP_Archive_Exception('invalid phar "' $this->_archiveName '"'$errors);
  373.         }
  374.     }
  375.  
  376.     /**
  377.      * Display information on a phar
  378.      *
  379.      * @param bool 
  380.      */
  381.     public function dump($return_array = false)
  382.     {
  383.         if (!$return_array{
  384.             echo $this;
  385.             return;
  386.         }
  387.         $filesize filesize($this->_archiveName);
  388.         $ret = array(
  389.             'Phar name' => $this->_archiveName,
  390.             'Size' => $filesize,
  391.             'API version' => $this->_apiVersion,
  392.             'Manifest size (bytes)' => $this->_manifestSize,
  393.             'Manifest entries' => count($this->_manifest),
  394.             'Alias' => $this->_alias,
  395.             'Phar Metadata' => var_export($this->_metadatatrue),
  396.             'Global compressed flag' => bin2hex($this->_flags),
  397.         );
  398.         if ($this->_signature{
  399.             $ret['Signature Type'$this->_sigtype;
  400.             $ret['Signature'$this->_signature;
  401.         }
  402.         // 0 = uncompressed file size
  403.         // 1 = save timestamp
  404.         // 2 = compressed file size
  405.         // 3 = crc32
  406.         // 4 = flags
  407.         // 5 = meta-data length
  408.         // 6 = meta-data
  409.         $offset = 0;
  410.         foreach ($this->_manifest as $file => $info{
  411.             $ret['File phar://' $this->_alias '/' $file ' size'$info[0];
  412.             $ret['File phar://' $this->_alias '/' $file ' save date'=
  413.                 date('Y-m-d H:i'$info[1]);
  414.             $ret['File phar://' $this->_alias '/' $file ' crc'$info[3];
  415.             $ret['File phar://' $this->_alias '/' $file ' size in archive'$info[2];
  416.             $ret['File phar://' $this->_alias '/' $file ' offset in archive'$offset;
  417.             $ret['File phar://' $this->_alias '/' $file ' meta-data length'$info[5];
  418.             $ret['File phar://' $this->_alias '/' $file ' meta-data'=
  419.                 var_export($info[6]true);
  420.             $ret['File phar://' $this->_alias '/' $file ' GZ compressed'=
  421.                 $info[4self::GZ ? 'yes' 'no';
  422.             $ret['File phar://' $this->_alias '/' $file ' BZ2 compressed'=
  423.                 $info[4self::BZ2 ? 'yes' 'no';
  424.             $offset += $info[2];
  425.         }
  426.         return $ret;
  427.     }
  428.  
  429.     public function __toString()
  430.     {
  431.         $ret $this->dump(true);
  432.         if ($this->_html{
  433.             array_walk($retcreate_function('&$a, $b''$a = "<strong>$b:</strong> $a";'));
  434.             $ret implode("<br />\n"$ret);
  435.         else {
  436.             array_walk($retcreate_function('&$a, $b''$a = "$b: $a";'));
  437.             $ret implode("\n"$ret);
  438.         }
  439.         return $ret;
  440.     }
  441.  
  442.     /**
  443.      * Extract the .phar to a particular location
  444.      *
  445.      * @param string $toHere 
  446.      */
  447.     public function unPhar($toHere)
  448.     {
  449.         
  450.     }
  451.  
  452.     /**
  453.      * Re-make the phar from a previously after having done work on an unPharred phar
  454.      *
  455.      * @param string $fromHere 
  456.      */
  457.     public function rePhar($fromHere)
  458.     {
  459.         
  460.     }
  461.  
  462.     /**
  463.      * For display of data in a browser
  464.      *
  465.      * @return PHP_Archive_Manager 
  466.      */
  467.     public function inHtml()
  468.     {
  469.         $this->_html = true;
  470.         $a = clone $this;
  471.         $this->_html = false;
  472.         return $a;
  473.     }
  474. }
  475. ?>

Documentation generated on Mon, 19 May 2008 11:30:16 -0400 by phpDocumentor 1.4.0. PEAR Logo Copyright © PHP Group 2004.