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

Source for file Creator.php

Documentation is available at Creator.php

  1. <?php
  2. /**
  3.  * PHP_Archive Class creator (creates .phar)
  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.php';
  13. /**
  14.  * PHP_Archive Class creator (implements .phar)
  15.  *
  16.  * PHAR files a singular archive from which an entire application can run.
  17.  * To use it, simply package it using {@see PHP_Archive_Creator} and use phar://
  18.  * URIs to your includes. i.e. require_once 'phar://config.php' will include config.php
  19.  * from the root of the PHAR file.
  20.  *
  21.  * Gz code borrowed from the excellent File_Archive package by Vincent Lascaux.
  22.  *
  23.  * @copyright Copyright ? David Shafik and Synaptic Media 2004. All rights reserved.
  24.  * @author Davey Shafik <davey@synapticmedia.net>
  25.  * @author Greg Beaver <cellog@php.net>
  26.  * @link http://www.synapticmedia.net Synaptic Media
  27.  * @version $Id: Creator.php,v 1.44 2008/05/19 15:16:55 cellog Exp $
  28.  * @package PHP_Archive
  29.  * @category PHP
  30.  */
  31. {
  32.     /**
  33.      * @var string The Archive Filename
  34.      */
  35.     protected $archive_name;
  36.  
  37.     /**
  38.      * @var string The temporary path to the TAR archive
  39.      */
  40.     protected $temp_path;
  41.  
  42.     /**
  43.      * @var string Where the TAR archive will be saved
  44.      */
  45.     protected $save_path;
  46.  
  47.     /**
  48.      * @var string The phar alias
  49.      */
  50.     protected $alias;
  51.     
  52.     /**
  53.      * @var boolean Whether or not the archive should be compressed
  54.      */
  55.     protected $compress = false;
  56.  
  57.     /**
  58.      * Phar bitmapped flags
  59.      *
  60.      * @var int 
  61.      */
  62.     protected $flags = 0;
  63.     /**
  64.      * @var boolean Whether or not to collapse (remove whitespace/comments)
  65.      */
  66.     protected $collapse = false;
  67.  
  68.     /**
  69.      * @var boolean Whether or not a file has been added to the archive
  70.      */
  71.     protected $modified = false;
  72.  
  73.     /**
  74.      * Used to construct the internal manifest, or listing of files/directories
  75.      *
  76.      * @var array 
  77.      */
  78.     protected $manifest = array();
  79.  
  80.     protected $relyOnPhar = false;
  81.     protected $initFile = false;
  82.  
  83.     /**
  84.      * Used to save Phar-specific metadata
  85.      * @var mixed 
  86.      */
  87.     protected $metadata = null;
  88.  
  89.     /**
  90.      * Signature type, either PHP_Archive::SHA1 or PHP_Archive::MD5
  91.      *
  92.      * @var int 
  93.      */
  94.     protected $sig;
  95.  
  96.     /**
  97.      * A list of custom callbacks that should be used for manipulating file contents
  98.      * prior to adding to the phar.
  99.      *
  100.      * @var array 
  101.      */
  102.     private $_magicRequireCallbacks = array();
  103.  
  104.     /**
  105.      * @param string 
  106.      */
  107.     public static function processFile($path)
  108.     {
  109.         if ($path == '.'{
  110.             return '';
  111.         }
  112.         $std str_replace("\\""/"$path);
  113.         while ($std != ($std ereg_replace("[^\/:?]+/\.\./"""$std))) ;
  114.         $std str_replace("/./"""$std);
  115.         if (strlen($std> 1 && $std[0== '/'{
  116.             $std substr($std1);
  117.         }
  118.         if (strncmp($std"./"2== 0{
  119.             return substr($std2);
  120.         else {
  121.             return $std;
  122.         }
  123.     }
  124.  
  125.     /**
  126.      * PHP_Archive Constructor
  127.      *
  128.      * @param string|false$init_file Init file (file called by default upon PHAR execution).
  129.      *                                 if false, none will be called, and execution will return.
  130.      *                                 use this option for libraries
  131.      * @param string $alias alias name like "go-pear.phar" to be used for opening
  132.      *                       files from this phar
  133.      * @param string|false$compress Whether to compress the files or not (will cause slowdown!)
  134.      *                                use 'gz' for zlib compression, 'bz2' for bzip2 compression
  135.      * @param bool $relyOnPhar if true, then a slim, phar extension-dependent .phar will be
  136.      *                          created
  137.      * @param bool $collapse Remove whitespace and comments from PHP_Archive class
  138.      */
  139.     public function __construct($init_file 'index.php'$alias$compress = false,
  140.                                 $relyOnPhar = false$collapse = false)
  141.     {
  142.         $this->compress = $compress;
  143.         $this->collapse = $collapse;
  144.         $this->relyOnPhar = $relyOnPhar;
  145.         $this->initFile = $init_file;
  146.         $this->temp_path = System::mktemp(array('-d''phr'));
  147.         $contents file_get_contents(dirname(dirname(__FILE__)) .
  148.             DIRECTORY_SEPARATOR . 'Archive.php');
  149.         if ($this->collapse{
  150.             $contents = self::collapse($contents);
  151.         }
  152.         $contents trim(str_replace(array('<?php''?>')array('''')$contents));
  153.         // make sure .phars added to CVS don't get checksum errors because of CVS tags
  154.         $contents str_replace('* @version $Id''* @version Id'$contents);
  155.         $unpack_code "<?php
  156. error_reporting(1803);
  157. if (function_exists('mb_internal_encoding')) {
  158.     mb_internal_encoding('ASCII');
  159. }
  160. ";
  161.         if (!$relyOnPhar{
  162.             // for smooth use of phar extension
  163.             $unpack_code .= "if (!class_exists('PHP_Archive')) {";
  164.             $unpack_code .= $contents;
  165.             $unpack_code .= "}
  166. if (!class_exists('Phar')) {
  167.     PHP_Archive::mapPhar(null, __COMPILER_HALT_OFFSET__);
  168. } else {
  169.     try {
  170.         Phar::mapPhar();
  171.     } catch (Exception \$e) {
  172.         echo \$e->getMessage();
  173.     }
  174. }
  175. if (class_exists('PHP_Archive') && !in_array('phar', stream_get_wrappers())) {
  176.     stream_wrapper_register('phar', 'PHP_Archive');
  177. }
  178. ";
  179.         else {
  180.             $unpack_code .= "if (!extension_loaded('phar')) {";
  181.             $unpack_code .= 'die("Error - phar extension not loaded");
  182. }
  183. try {
  184.     Phar::mapPhar();
  185. } catch (Exception \$e) {
  186.     echo \$e->getMessage();
  187. }
  188. ';
  189.         }
  190.         $unpack_code .= "\n@ini_set('memory_limit', -1);\n";
  191.  
  192.         $this->alias = $alias;
  193.         if ($init_file{
  194.             $unpack_code .= '
  195.  
  196. require_once \'phar://@ALIAS@/' addslashes($init_file'\';
  197. ';
  198.         }
  199.         $unpack_code .= '__HALT_COMPILER();';
  200.         file_put_contents($this->temp_path . DIRECTORY_SEPARATOR . 'loader.php'$unpack_code);
  201.     }
  202.  
  203.     /**
  204.      * Set meta-data for entire Phar archive
  205.      *
  206.      * @param mixed $metadata 
  207.      */
  208.     public function setPharMetadata($metadata)
  209.     {
  210.         $this->_metadata $metadata;
  211.     }
  212.  
  213.     /**
  214.      * Append a signature to this phar when it is created
  215.      */
  216.     public function useSHA1Signature()
  217.     {
  218.         $this->flags |= PHP_Archive::SIG;
  219.         $this->sig = PHP_Archive::SHA1;
  220.     }
  221.  
  222.     /**
  223.      * Append a signature to this phar when it is created
  224.      */
  225.     public function useMD5Signature()
  226.     {
  227.         $this->flags |= PHP_Archive::SIG;
  228.         $this->sig = PHP_Archive::MD5;
  229.     }
  230.  
  231.     /**
  232.      * Specify a custom "magic require" callback for processing file contents.
  233.      * 
  234.      * This will be called regardless of the magicrequire parameter's
  235.      * value for {@link addString()} or {@link addFile()}
  236.      *
  237.      * @param callback $callback 
  238.      */
  239.     public function addMagicRequireCallback($callback)
  240.     {
  241.         if (is_callable($callback)) {
  242.             $this->_magicRequireCallbacks[$callback;
  243.         }
  244.     }
  245.  
  246.     /**
  247.      * Add a file to the PHP Archive
  248.      *
  249.      * @param string $file Path of the File to add
  250.      * @param string $save_path The save location of the file in the archive
  251.      * @param false  $magicrequire unused, set this to false
  252.      * @param mixed  $metadata Any file-specific metadata to save
  253.      * @return boolean 
  254.      */
  255.     
  256.     public function addFile($file$save_path$magicrequire = false$metadata = null)
  257.     {
  258.         return $this->addString(file_get_contents($file)$save_path$magicrequire$metadata);
  259.     }
  260.  
  261.     /**
  262.      * For web-based applications, construct a default front controller
  263.      * that will direct to the correct file within the phar.
  264.      *
  265.      * @param string  $indexfile    relative path to startup index file (defaults to the same file as
  266.      *                               is used for CLI startup)
  267.      * @param array   $defaultmimes list of MIME types to use, associative array of
  268.      *                               extension => mime type. default is
  269.      *                               from {@link PHP_Archive::$defaultmimes}
  270.      * @param array   $defaultphp   list of file extensions that should be parsed as PHP. default is
  271.      *                               from {@link PHP_Archive::$defaultphp}
  272.      * @param array   $defaultphps  list of file extensions that should be parsed as PHP source. default is
  273.      *                               from {@link PHP_Archive::$defaultphps}
  274.      * @param array   $deny         list of files that should be hidden (return a 404 response)
  275.      *                               Each file should be a valid pcre regular expression like '/.+\.inc$/',
  276.      *                               which will deny all .inc files from being served.  The default is
  277.      *                               from {@link PHP_Archive::$deny}
  278.      */
  279.     public function useDefaultFrontController($indexfile = false$defaultmimes = false$defaultphp = false,
  280.                     $defaultphps = false$deny = false)
  281.     {
  282.         if (!$indexfile{
  283.             $indexfile $this->initFile;
  284.         }
  285.         $contents file_get_contents($this->temp_path . DIRECTORY_SEPARATOR . 'loader.php');
  286.         if ($this->relyOnPhar{
  287.             if (!$defaultmimes{
  288.                 $defaultmimes PHP_Archive::$defaultmimes;
  289.             }
  290.             if (!$defaultphp{
  291.                 $defaultphp = PHP_Archive::$defaultphp;
  292.             }
  293.             if (!$defaultphps{
  294.                 $defaultphps = PHP_Archive::$defaultphps;
  295.             }
  296.             if (!$deny{
  297.                 $deny = PHP_Archive::$deny;
  298.             }
  299.             $templatefile '@data_dir@/PHP_Archive/data/phar_frontcontroller.tpl';
  300.             $template = file_get_contents($templatefile);
  301.             if (!$template{
  302.                 throw new PHP_Archive_Exception('Invalid template file "' $templatefile '"');
  303.             }
  304.             $template str_replace('@mime@'var_export($defaultmimestrue)$template);
  305.             $template str_replace('@php@'var_export($defaultphptrue)$template);
  306.             $template str_replace('@phps@'var_export($defaultphpstrue)$template);
  307.             $template str_replace('@alias@'$this->alias$template);
  308.             $template str_replace('@deny@'var_export($denytrue)$template);
  309.             $template str_replace('@initfile@'$this->initFile$template);
  310.             $contents str_replace("@ini_set('memory_limit', -1);",
  311.                 "@ini_set('memory_limit', -1);\n" $template "\n");
  312.         else {
  313.             $extra '';
  314.             if ($defaultmimes || $defaultphp || $defaultphps || $deny{
  315.                 if ($defaultmimes{
  316.                     $extra .= "\nPHP_Archive::\$defaultmimes = " .
  317.                         var_export($defaultmimestrue"\n";
  318.                 }
  319.                 if ($defaultphp{
  320.                     $extra .= "\nPHP_Archive::\$defaultphp = " .
  321.                         var_export($defaultphptrue"\n";
  322.                 }
  323.                 if ($defaultphps{
  324.                     $extra .= "\nPHP_Archive::\$defaultphps = " .
  325.                         var_export($defaultphpstrue"\n";
  326.                 }
  327.                 if ($deny{
  328.                     $extra .= "\nPHP_Archive::\$deny = " .
  329.                         var_export($denytrue"\n";
  330.                 }
  331.             }
  332.             // if Phar extension is present, use the template code instead
  333.             if (!$defaultmimes{
  334.                 $defaultmimes PHP_Archive::$defaultmimes;
  335.             }
  336.             if (!$defaultphp{
  337.                 $defaultphp = PHP_Archive::$defaultphp;
  338.             }
  339.             if (!$defaultphps{
  340.                 $defaultphps = PHP_Archive::$defaultphps;
  341.             }
  342.             if (!$deny{
  343.                 $deny = PHP_Archive::$deny;
  344.             }
  345.             $templatefile '@data_dir@/PHP_Archive/data/phar_frontcontroller.tpl';
  346.             $template = file_get_contents($templatefile);
  347.             if (!$template{
  348.                 throw new PHP_Archive_Exception('Invalid template file "' $templatefile '"');
  349.             }
  350.             $template str_replace('@mime@'var_export($defaultmimestrue)$template);
  351.             $template str_replace('@php@'var_export($defaultphptrue)$template);
  352.             $template str_replace('@phps@'var_export($defaultphpstrue)$template);
  353.             $template str_replace('@alias@'$this->alias$template);
  354.             $template str_replace('@deny@'var_export($denytrue)$template);
  355.             $template str_replace('@initfile@'$indexfile$template);
  356.             
  357.             $contents str_replace("@ini_set('memory_limit', -1);",
  358.                 "@ini_set('memory_limit', -1);\n" .
  359.                 'if (extension_loaded(\'phar\')) {' $template '} else {' .
  360.                 $extra .
  361.                 "if (!empty(\$_SERVER['REQUEST_URI'])) " .
  362.                 "{PHP_Archive::webFrontController('" .
  363.                 addslashes($indexfile"');exit;}}\n"$contents);
  364.         }
  365.         file_put_contents($this->temp_path . DIRECTORY_SEPARATOR . 'loader.php',
  366.             $contents);
  367.     }
  368.  
  369.     /**
  370.      * Add a string to the PHP Archive as a file
  371.      *
  372.      * @param string $file_contents Contents of the File to add
  373.      * @param string $save_path The save location of the file in the archive
  374.      * @return boolean 
  375.      */
  376.     
  377.     public function addString($file_contents$save_path$magicrequire = false,
  378.                               $metadata = null)
  379.     {
  380.         $save_path = self::processFile($save_path);
  381.         if (count($this->_magicRequireCallbacks)) {
  382.             foreach ($this->_magicRequireCallbacks as $callback{
  383.                 $file_contents call_user_func($callback$file_contents$save_path);
  384.             }
  385.         }
  386.         if ($magicrequire{
  387.             die('ERROR: magicrequire is removed, set a magicrequire callback to ' .
  388.                 'array("PHP_Archive_Creator", "simpleMagicRequire) to implement');
  389.         }
  390.         if (!file_exists($this->temp_path . DIRECTORY_SEPARATOR . 'contents')) {
  391.             mkdir($this->temp_path . DIRECTORY_SEPARATOR . 'contents');
  392.         }
  393.         $size strlen($file_contents);
  394.         $crc32 crc32($file_contents);
  395.         // save crc32 of file and the uncompressed file size, so we
  396.         // can do a sanity check on the file when opening it from the phar
  397.         if ($this->compress{
  398.             if ($this->compress == 'gz'{
  399.                 $this->flags |= PHP_Archive::GZ;
  400.                 $file_contents gzdeflate($file_contents9);
  401.             elseif ($this->compress == 'bz2'{
  402.                 $this->flags |= PHP_Archive::BZ2;
  403.                 $file_contents = bzcompress($file_contents9);
  404.             }
  405.         }
  406.         System::mkdir(array('-p'dirname($this->temp_path . DIRECTORY_SEPARATOR . 'contents' .
  407.             DIRECTORY_SEPARATOR . $save_path)));
  408.         if (file_exists($this->temp_path . DIRECTORY_SEPARATOR . 'contents' .
  409.               DIRECTORY_SEPARATOR . $save_path)) {
  410.             die('ERROR: path "' $save_path '" already exists');
  411.         }
  412.         file_put_contents($this->temp_path . DIRECTORY_SEPARATOR . 'contents' .
  413.             DIRECTORY_SEPARATOR . $save_path$file_contents);
  414.         $flags = 0;
  415.         if ($this->compress{
  416.             $flags |= ($this->compress == 'gz'? 0x00001000 : 0x00002000;
  417.         }
  418.         $flags |= 0555; // file permissions
  419.         $this->manifest[$save_path=
  420.             array(
  421.                 'tempfile' => $this->temp_path . DIRECTORY_SEPARATOR . 'contents' .
  422.                     DIRECTORY_SEPARATOR . $save_path,
  423.                 'originalsize' => $size,
  424.                 'actualsize' => strlen($file_contents),
  425.                 'crc32' => $crc32,
  426.                 'flags' => $flags,
  427.                 'metadata' => $metadata);
  428.     }
  429.  
  430.     public function clearMagicRequire()
  431.     {
  432.         $this->_magicRequireCallbacks = array();
  433.     }
  434.  
  435.     /**
  436.      * prepends all include/require calls with "phar://alias"
  437.      *
  438.      * @param string $contents file contents
  439.      * @param string $path internal path of the file
  440.      */
  441.     public function simpleMagicRequire($contents$path)
  442.     {
  443.         $file_contents str_replace("require_once '""require_once 'phar://" $this->alias . "/",
  444.             $contents);
  445.         $file_contents str_replace("include_once '""include_once 'phar://" $this->alias . "/",
  446.             $file_contents);
  447.         return $file_contents;
  448.     }
  449.  
  450.     /**
  451.      * prepends "phar://alias" only to include/require that use quotes
  452.      *
  453.      * @param string $contents file contents
  454.      * @param string $path internal path of the file
  455.      */
  456.     public function limitedSmartMagicRequire($contents$path)
  457.     {
  458.         $file_contents preg_replace(
  459.             '/(include|require)(_once)?\s*((?:\'|")[^;]+);/',
  460.             '$1$2 \'phar://' $this->alias . '/\' . $3;'$contents);
  461.         return $file_contents;
  462.     }
  463.  
  464.     /**
  465.      * The basic magicrequire callback (implements old-fashioned magicrequire)
  466.      *
  467.      * @param string $contents file contents
  468.      * @param string $path internal path of the file
  469.      */
  470.     public function smartMagicRequire($contents$path)
  471.     {
  472.         $file_contents preg_replace(
  473.             '/(include|require)(_once)?([^;]+);/',
  474.             '$1$2 \'phar://' $this->alias . '/\' . $3'$contents);
  475.         return $file_contents;
  476.     }
  477.  
  478.     public function tokenMagicRequire($contents$path)
  479.     {
  480.         $info pathinfo($path);
  481.         if (!isset($info['extension'])) {
  482.             return $contents;
  483.         }
  484.         if ($info['extension'!= 'php'{
  485.             return $contents;
  486.         }
  487.         $alias '\'phar://' $this->alias . '/\' . ';
  488.         $contents token_get_all($contents);
  489.         $inphp = false;
  490.         $finished '';
  491.         for ($i = 0; $i count($contents)$i++{
  492.             $token $contents[$i];
  493.             if (!$inphp{
  494.                 if (!is_array($token)) {
  495.                     $finished .= $token;
  496.                     continue;
  497.                 }
  498.                 if ($token[0== T_OPEN_TAG{
  499.                     $finished .= $token[1];
  500.                     $inphp = true;
  501.                     continue;
  502.                 }
  503.             }
  504.             if (!is_array($token)) {
  505.                 $finished .= $token;
  506.                 continue;
  507.             }
  508.             if ($token[0== T_CLOSE_TAG{
  509.                 $finished .= $token[1];
  510.                 $inphp = false;
  511.                 continue;
  512.             }
  513.             if ($token[0!= T_INCLUDE && $token[0!= T_INCLUDE_ONCE &&
  514.                   $token[0!= T_REQUIRE && $token[0!= T_REQUIRE_ONCE{
  515.                 $finished .= $token[1];
  516.                 continue;
  517.             }
  518.             $finished .= $token[1];
  519.             $new '';
  520.             $done = false;
  521.             $token $contents[++$i];
  522.             for (;!$done;$i++{
  523.                 $token $contents[$i];
  524.                 if (!is_array($token)) {
  525.                     if ($token == '(' || $token == '{' || $token == '"' ||
  526.                           $token == "'"{
  527.                         $finished .= ' ' $alias $token;
  528.                         continue 2;
  529.                     }
  530.                     $finished .= $token;
  531.                     // we have some oddness, don't touch this with a 10-foot pole
  532.                     continue 2;
  533.                 }
  534.                 if ($token[0== T_WHITESPACE{
  535.                     $finished .= $token[1];
  536.                     continue;
  537.                 }
  538.                 $done = true;
  539.                 $finished .= $alias $token[1];
  540.                 $i--;
  541.             }
  542.         }
  543.         return $finished;
  544.     }
  545.     /**
  546.      * Tell whether to ignore a file or a directory
  547.      * allows * and ? wildcards
  548.      *
  549.      * @param    string  $file    just the file name of the file or directory,
  550.      *                           in the case of directories this is the last dir
  551.      * @param    string  $path    the full path
  552.      * @param    1|0   $return  value to return if regexp matches.  Set this to
  553.      *                             false to include only matches, true to exclude
  554.      *                             all matches
  555.      * @return   bool    true if $path should be ignored, false if it should not
  556.      */
  557.     private function _checkIgnore($file$path$return = 1)
  558.     {
  559.         if (file_exists($path)) {
  560.             $path realpath($path);
  561.         }
  562.         if (is_array($this->ignore[$return])) {
  563.             foreach($this->ignore[$returnas $match{
  564.                 // match is an array if the ignore parameter was a /path/to/pattern
  565.                 if (is_array($match)) {
  566.                     // check to see if the path matches with a path delimiter appended
  567.                     preg_match('/^' strtoupper($match[0]).'$/'strtoupper($path'/',$find);
  568.                     if (!count($find)) {
  569.                         // check to see if it matches without an appended path delimiter
  570.                         preg_match('/^' strtoupper($match[0]).'$/'strtoupper($path)$find);
  571.                     }
  572.                     if (count($find)) {
  573.                         // check to see if the file matches the file portion of the regex string
  574.                         preg_match('/^' strtoupper($match[1]).'$/'strtoupper($file)$find);
  575.                         if (count($find)) {
  576.                             return $return;
  577.                         }
  578.                     }
  579.                     // check to see if the full path matches the regex
  580.                     preg_match('/^' strtoupper($match[0]).'$/',
  581.                                strtoupper($path . DIRECTORY_SEPARATOR . $file)$find);
  582.                     if (count($find)) {
  583.                         return $return;
  584.                     }
  585.                 else {
  586.                     // ignore parameter was just a pattern with no path delimiters
  587.                     // check it against the path
  588.                     preg_match('/^' strtoupper($match).'$/'strtoupper($path)$find);
  589.                     if (count($find)) {
  590.                         return $return;
  591.                     }
  592.                     // check it against the file only
  593.                     preg_match('/^' strtoupper($match).'$/'strtoupper($file)$find);
  594.                     if (count($find)) {
  595.                         return $return;
  596.                     }
  597.                 }
  598.             }
  599.         }
  600.         return !$return;
  601.     }
  602.     
  603.     /**
  604.      * Construct the {@link $ignore} array
  605.      * @param array strings of files/paths/wildcards to ignore
  606.      * @param 0|10 = files to include, 1 = files to ignore
  607.      */
  608.     private function _setupIgnore($ignore$index)
  609.     {
  610.         $ig = array();
  611.         if (is_array($ignore)) {
  612.             for($i=0; $i<count($ignore);$i++{
  613.                 $ignore[$istrtr($ignore[$i]"\\""/");
  614.                 $ignore[$istr_replace('//','/',$ignore[$i]);
  615.  
  616.                 if (!empty($ignore[$i])) {
  617.                     if (!is_numeric(strpos($ignore[$i]'/'))) {
  618.                         $ig[$this->_getRegExpableSearchString($ignore[$i]);
  619.                     else {
  620.                         if (basename($ignore[$i]'/' == $ignore[$i]{
  621.                             $ig[$this->_getRegExpableSearchString($ignore[$i]);
  622.                         else {
  623.                             $ig[= array($this->_getRegExpableSearchString($ignore[$i]),
  624.                                       $this->_getRegExpableSearchString(basename($ignore[$i])));
  625.                         }
  626.                     }
  627.                 }
  628.             }
  629.             if (count($ig)) {
  630.                 $this->ignore[$index$ig;
  631.             else {
  632.                 $this->ignore[$index= false;
  633.             }
  634.         else $this->ignore[$index= false;
  635.     }
  636.     
  637.     /**
  638.      * Converts $s into a string that can be used with preg_match
  639.      * @param string $s string with wildcards ? and *
  640.      * @return string converts * to .*, ? to ., etc.
  641.      */
  642.     private function _getRegExpableSearchString($s)
  643.     {
  644.         $y '\/';
  645.         if (DIRECTORY_SEPARATOR == '\\'{
  646.             $y '\\\\';
  647.         }
  648.         $s str_replace('/'DIRECTORY_SEPARATOR$s);
  649.         $x strtr($sarray('?' => '.','*' => '.*','.' => '\\.','\\' => '\\\\','/' => '\\/',
  650.                                 '[' => '\\[',']' => '\\]','-' => '\\-'));
  651.         if (strpos($sDIRECTORY_SEPARATOR!== false &&
  652.               strrpos($sDIRECTORY_SEPARATOR=== strlen($s- 1{
  653.             $x = "(?:.*$y$x?.*|$x.*)";
  654.         }
  655.         return $x;
  656.     }
  657.  
  658.     /**
  659.      * Test whether an entry should be processed.
  660.      * 
  661.      * Normally, it ignores all files and directories that begin with "."  addhiddenfiles option
  662.      * instead only ignores "." and ".." entries
  663.      * @param string directory name of entry
  664.      * @param string name
  665.      */
  666.     private function _testFile($directory$entry)
  667.     {
  668.         return is_file($directory '/' $entry||
  669.               (is_dir($directory '/' $entry&& !in_array($entryarray('.''..')));
  670.     }
  671.  
  672.     /**
  673.      * Retrieve a listing of every file in $directory and
  674.      * all subdirectories.
  675.      *
  676.      * The return format is an array of full paths to files
  677.      * @access protected
  678.      * @return array list of files in a directory
  679.      * @param string $directory full path to the directory you want the list of
  680.      */
  681.     public function dirList($directory$toplevel = null)
  682.     {
  683.         if ($toplevel === null{
  684.             $toplevel $directory;
  685.         }
  686.         $ret = false;
  687.         $dirname str_replace($toplevel . DIRECTORY_SEPARATOR''$directory);
  688.         $dirname str_replace($toplevel''$dirname);
  689.         if ($dirname{
  690.             $dirname .= DIRECTORY_SEPARATOR;
  691.         }
  692.         if (@is_dir($directory)) {
  693.             $ret = array();
  694.             $d @dir($directory);
  695.             while($d && false !== ($entry $d->read())) {
  696.                 if ($this->_testFile($directory$entry)) {
  697.                     if (is_file($directory '/' $entry)) {
  698.                         // if include option was set, then only pass included files
  699.                         if ($this->ignore[0]{
  700.                             if ($this->_checkIgnore($entry$directory '/' $entry0)) {
  701.                                 continue;
  702.                             }
  703.                         }
  704.                         // if ignore option was set, then only pass included files
  705.                         if ($this->ignore[1]{
  706.                             if ($this->_checkIgnore($entry$directory '/' $entry1)) {
  707.                                 continue;
  708.                             }
  709.                         }
  710.                         $ret[$directory . DIRECTORY_SEPARATOR . $entry$dirname $entry;
  711.                     }
  712.                     if (is_dir($directory '/' $entry)) {
  713.                         $tmp $this->dirList($directory . DIRECTORY_SEPARATOR . $entry$toplevel);
  714.                         if (is_array($tmp)) {
  715.                             foreach($tmp as $i => $ent{
  716.                                 $ret[$i$ent;
  717.                             }
  718.                         }
  719.                     }
  720.                 }
  721.             }
  722.             if ($d{
  723.                 $d->close();
  724.             }
  725.         else {
  726.             return false;
  727.         }
  728.         return $ret;
  729.     }
  730.  
  731.     /**
  732.      * Add a directory to the archive
  733.      *
  734.      * @param string The directory path to add
  735.      * @param array  files to ignore
  736.      * @param array  files to include (all others ignored)
  737.      * @param bool   If set, then "require_once '" will be replaced with
  738.      *                "require_once 'phar://$magicrequire/" [deprecated]
  739.      * @param string Directory to consider as the top-level directory
  740.      * @return boolean 
  741.      */
  742.     
  743.     public function addDir($dir$ignore = array()$include = array()$magicrequire = false,
  744.                            $toplevel = null)
  745.     {
  746.         $this->_setupIgnore($ignore1);
  747.         $this->_setupIgnore($include0);
  748.         $list $this->dirList($dir$toplevel);
  749.         return $this->addArray($list$magicrequire);
  750.     }
  751.  
  752.     /**
  753.      * Collapse a block of code (remove whitespace and comments)
  754.      *
  755.      * @param string $contents PHP code to collapse
  756.      * @return string 
  757.      * @author Sean Coates <sean@php.net>
  758.      */
  759.     private static function collapse($contents)
  760.     {
  761.         $ret '';
  762.         $tokens token_get_all($contents);
  763.         foreach ($tokens as $t{
  764.             if (is_string($t)) {
  765.                 $ret .= $t;
  766.             else {
  767.                 list($token$data$t;
  768.                 if ($token == T_WHITESPACE{
  769.                     if (strpos($data"\n"!== false{
  770.                         $data "\n";
  771.                     else {
  772.                         $data ' ';
  773.                     }
  774.                 elseif ($token == T_COMMENT || $token == T_DOC_COMMENT{
  775.                     $data '';
  776.                 }
  777.                 $ret .= $data;
  778.             }
  779.         }
  780.         return $ret;
  781.     }
  782.  
  783.     /**
  784.      * add an array of files to the archive
  785.      *
  786.      * @param unknown_type $files 
  787.      * @param bool $magicrequire determines whether to attempt to replace all
  788.      *                            calls to require or include with internal
  789.      *                            phar includes
  790.      * @return unknown 
  791.      */
  792.     public function addArray($files$magicrequire = false)
  793.     {
  794.         if (!is_array($files|| empty($files)) {
  795.             return false;
  796.         }
  797.         foreach ($files as $file_path => $save_path{
  798.             $returns[$this->addFile($file_path$save_path$magicrequire);
  799.         }
  800.         return !in_array(false$returns);
  801.     }
  802.  
  803.     /**
  804.      * Construct the .phar and save it
  805.      *
  806.      * @param string $file_path 
  807.      * @return bool success of operation
  808.      */
  809.     public function savePhar($file_path)
  810.     {
  811.         uksort($this->manifest'strnatcasecmp');
  812.         $newfile fopen($file_path'wb');
  813.         if (!$newfile{
  814.             return false;
  815.         }
  816.         if (isset($this->alias)) {
  817.             $loader file_get_contents($this->temp_path . DIRECTORY_SEPARATOR . 'loader.php');
  818.             $loader str_replace('@ALIAS@'addslashes($this->alias)$loader);
  819.         else {
  820.             $loader file_get_contents($this->temp_path . DIRECTORY_SEPARATOR . 'loader.php');
  821.             $loader str_replace('@ALIAS@'addslashes(basename($file_path))$loader);
  822.             $this->alias = addslashes(basename($file_path));
  823.         }
  824.         $loader str_replace('__COMPILER_HALT_OFFSET__'str_pad(strlen($loader),
  825.             strlen('__COMPILER_HALT_OFFSET__'))$loader);
  826.         fwrite($newfile$loader);
  827.  
  828.         // relative offset from end of manifest
  829.         $offset = 0;
  830.         $manifest = array();
  831.         // create the manifest
  832.         foreach ($this->manifest as $savepath => $info{
  833.             $size $info['originalsize'];
  834.             $manifest[$savepath= array(
  835.                 $size// original size = 0
  836.                 time()// save time = 1
  837.                 $info['actualsize']// actual size in the .phar = 2
  838.                 $info['crc32']// crc32 = 3
  839.                 $info['flags']// flags = 4
  840.                 $info['metadata'])// metadata = 5
  841.             $offset += $info['actualsize'];
  842.         }
  843.         $manifest $this->serializeManifest($manifest);
  844.         fwrite($newfile$manifest);
  845.         // save each file
  846.         foreach ($this->manifest as $savepath => $info{
  847.             $file fopen($info['tempfile']'rb');
  848.             stream_copy_to_stream($file$newfile);
  849.             fclose($file);
  850.         }
  851.         fclose($newfile);
  852.         if ($this->flags PHP_Archive::SIG{
  853.             if ($this->sig == PHP_Archive::SHA1{
  854.                 $sig sha1_file($file_pathtrue);
  855.             elseif ($this->sig == PHP_Archive::MD5{
  856.                 $sig md5_file($file_pathtrue);
  857.             }
  858.             $fp fopen($file_path'ab');
  859.             fwrite($fp$sig);
  860.             // add signature indicator plus the magic indicator
  861.             // ah to be immortalized in file format
  862.             fwrite($fppack('V'$this->sig'GBMB');
  863.             fclose($fp);
  864.         }
  865.         return true;
  866.     }
  867.  
  868.     /**
  869.      * serialize the manifest in a C-friendly way
  870.      *
  871.      * @param array $manifest An array like so:
  872.      *  <code>
  873.      *    $manifest[] = array(
  874.      *       $savepath,
  875.      *       $size, // original size = 0
  876.      *       time(), // save time = 1
  877.      *       $offset, // offset from start of files = 2
  878.      *       $info['actualsize']); // actual size in the .phar = 3
  879.      *  </code>
  880.      * @return string 
  881.      */
  882.     public function serializeManifest($manifest)
  883.     {
  884.         $apiver explode('.''@API-VER@');
  885.         // store API version and compression in 2 bytes
  886.         $apiver chr(((int) ((int) $apiver[0]<< 4((int) $apiver[1])) .
  887.             chr((int)($apiver[2<< 4($this->compress ? 0x1 : 0));
  888.         $ret $apiver;
  889.         $ret .= pack('V'$this->flags);
  890.         $ret .= pack('V'strlen($this->alias)) $this->alias;
  891.         if ($this->metadata === null{
  892.             $ret .= pack('V'0);
  893.         else {
  894.             $metadata serialize($this->metadata);
  895.             $ret .= pack('V'strlen($metadata)) $metadata;
  896.         }
  897.         foreach ($manifest as $savepath => $info{
  898.             // save the string length, then the string, then this info
  899.             // uncompressed file size
  900.             // save timestamp
  901.             // compressed file size
  902.             // crc32
  903.             // flags
  904.             // metadata
  905.             $metadata array_pop($info);
  906.             $ret .= pack('V'strlen($savepath)) $savepath call_user_func_array('pack',
  907.                 array_merge(array('VVVVV')$info));
  908.             if ($metadata === null{
  909.                 $ret .= pack('V'0);
  910.             else {
  911.                 $metadata serialize($metadata);
  912.                 $ret .= pack('V'strlen($metadata)) $metadata;
  913.             }
  914.         }
  915.         // save the size of the manifest
  916.         return pack('VV'strlen($ret+ 4count($manifest)) $ret;
  917.     }
  918. }
  919. ?>

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