BasePhpFormulaLoader.php 4.8KB

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