vendor/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of the Assetic package, an OpenSky project.
       
     5  *
       
     6  * (c) 2010-2011 OpenSky Project Inc
       
     7  *
       
     8  * For the full copyright and license information, please view the LICENSE
       
     9  * file that was distributed with this source code.
       
    10  */
       
    11 
       
    12 namespace Assetic\Factory\Loader;
       
    13 
       
    14 use Assetic\Factory\AssetFactory;
       
    15 use Assetic\Factory\Resource\ResourceInterface;
       
    16 
       
    17 /**
       
    18  * Loads asset formulae from PHP files.
       
    19  *
       
    20  * @author Kris Wallsmith <kris.wallsmith@gmail.com>
       
    21  */
       
    22 abstract class BasePhpFormulaLoader implements FormulaLoaderInterface
       
    23 {
       
    24     protected $factory;
       
    25     protected $prototypes;
       
    26 
       
    27     public function __construct(AssetFactory $factory)
       
    28     {
       
    29         $this->factory = $factory;
       
    30         $this->prototypes = array();
       
    31 
       
    32         foreach ($this->registerPrototypes() as $prototype => $options) {
       
    33             $this->addPrototype($prototype, $options);
       
    34         }
       
    35     }
       
    36 
       
    37     public function addPrototype($prototype, array $options = array())
       
    38     {
       
    39         $tokens = token_get_all('<?php '.$prototype);
       
    40         array_shift($tokens);
       
    41 
       
    42         $this->prototypes[$prototype] = array($tokens, $options);
       
    43     }
       
    44 
       
    45     public function load(ResourceInterface $resource)
       
    46     {
       
    47         if (!$nbProtos = count($this->prototypes)) {
       
    48             throw new \LogicException('There are no prototypes registered.');
       
    49         }
       
    50 
       
    51         $buffers = array_fill(0, $nbProtos, '');
       
    52         $bufferLevels = array_fill(0, $nbProtos, 0);
       
    53         $buffersInWildcard = array();
       
    54 
       
    55         $tokens = token_get_all($resource->getContent());
       
    56         $calls = array();
       
    57 
       
    58         while ($token = array_shift($tokens)) {
       
    59             $current = self::tokenToString($token);
       
    60             // loop through each prototype (by reference)
       
    61             foreach (array_keys($this->prototypes) as $i) {
       
    62                 $prototype =& $this->prototypes[$i][0];
       
    63                 $options = $this->prototypes[$i][1];
       
    64                 $buffer =& $buffers[$i];
       
    65                 $level =& $bufferLevels[$i];
       
    66 
       
    67                 if (isset($buffersInWildcard[$i])) {
       
    68                     switch ($current) {
       
    69                         case '(': ++$level; break;
       
    70                         case ')': --$level; break;
       
    71                     }
       
    72 
       
    73                     $buffer .= $current;
       
    74 
       
    75                     if (!$level) {
       
    76                         $calls[] = array($buffer.';', $options);
       
    77                         $buffer = '';
       
    78                         unset($buffersInWildcard[$i]);
       
    79                     }
       
    80                 } elseif ($current == self::tokenToString(current($prototype))) {
       
    81                     $buffer .= $current;
       
    82                     if ('*' == self::tokenToString(next($prototype))) {
       
    83                         $buffersInWildcard[$i] = true;
       
    84                         ++$level;
       
    85                     }
       
    86                 } else {
       
    87                     reset($prototype);
       
    88                     unset($buffersInWildcard[$i]);
       
    89                     $buffer = '';
       
    90                 }
       
    91             }
       
    92         }
       
    93 
       
    94         $formulae = array();
       
    95         foreach ($calls as $call) {
       
    96             $formulae += call_user_func_array(array($this, 'processCall'), $call);
       
    97         }
       
    98 
       
    99         return $formulae;
       
   100     }
       
   101 
       
   102     private function processCall($call, array $protoOptions = array())
       
   103     {
       
   104         $tmp = tempnam(sys_get_temp_dir(), 'assetic');
       
   105         file_put_contents($tmp, implode("\n", array(
       
   106             '<?php',
       
   107             $this->registerSetupCode(),
       
   108             $call,
       
   109             'echo serialize($_call);',
       
   110         )));
       
   111         $args = unserialize(shell_exec('php '.escapeshellarg($tmp)));
       
   112         unlink($tmp);
       
   113 
       
   114         $inputs  = isset($args[0]) ? self::argumentToArray($args[0]) : array();
       
   115         $filters = isset($args[1]) ? self::argumentToArray($args[1]) : array();
       
   116         $options = isset($args[2]) ? $args[2] : array();
       
   117 
       
   118         if (!isset($options['debug'])) {
       
   119             $options['debug'] = $this->factory->isDebug();
       
   120         }
       
   121 
       
   122         if (!is_array($options)) {
       
   123             throw new \RuntimeException('The third argument must be omitted, null or an array.');
       
   124         }
       
   125 
       
   126         // apply the prototype options
       
   127         $options += $protoOptions;
       
   128 
       
   129         if (!isset($options['name'])) {
       
   130             $options['name'] = $this->factory->generateAssetName($inputs, $filters, $options);
       
   131         }
       
   132 
       
   133         return array($options['name'] => array($inputs, $filters, $options));
       
   134     }
       
   135 
       
   136     /**
       
   137      * Returns an array of prototypical calls and options.
       
   138      *
       
   139      * @return array Prototypes and options
       
   140      */
       
   141     abstract protected function registerPrototypes();
       
   142 
       
   143     /**
       
   144      * Returns setup code for the reflection scriptlet.
       
   145      *
       
   146      * @return string Some PHP setup code
       
   147      */
       
   148     abstract protected function registerSetupCode();
       
   149 
       
   150     static protected function tokenToString($token)
       
   151     {
       
   152         return is_array($token) ? $token[1] : $token;
       
   153     }
       
   154 
       
   155     static protected function argumentToArray($argument)
       
   156     {
       
   157         return is_array($argument) ? $argument : array_filter(array_map('trim', explode(',', $argument)));
       
   158     }
       
   159 }