* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * sfClassManipulator manipulates class code. * * @package symfony * @subpackage util * @author Fabien Potencier * @version SVN: $Id: sfClassManipulator.class.php 25063 2009-12-08 06:02:07Z Kris.Wallsmith $ */ class sfClassManipulator { static protected $signatureTokens = array( T_FINAL, T_ABSTRACT, T_STATIC, T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FUNCTION, ); protected $code = '', $file = false; /** * Constructor. * * @param string $code The code to manipulate */ public function __construct($code) { $this->code = $code; } /** * Creates a manipulator object from a file. * * @param string $file A file name * * @return sfClassManipulator A sfClassManipulator instance */ static public function fromFile($file) { $manipulator = new self(file_get_contents($file)); $manipulator->setFile($file); return $manipulator; } /** * Saves the code back to the associated file. * * This only works if you have bound the instance with a file with the setFile() method. * * @throw LogicException if no file is associated with the instance */ public function save() { if (!$this->file) { throw new LogicException('Unable to save the code as no file has been provided.'); } file_put_contents($this->file, $this->code); } /** * Gets the modified code. * * @return string The modified code */ public function getCode() { return $this->code; } /** * Gets the associated file. * * @return string The associated file */ public function getFile() { return $this->file; } /** * Sets the file associated with this instance. * * @param string A file name */ public function setFile($file) { $this->file = $file; } /** * Wraps an existing method with some code. * * @param string $method The method name to change * @param string $topCode The code to add at the top of the method * @param string $bottomCode The code to add at the bottom of the method */ public function wrapMethod($method, $topCode = '', $bottomCode = '') { $code = ''; $insideSetup = -1; $parens = 0; foreach (token_get_all($this->code) as $token) { if (isset($token[1])) { if (-1 == $insideSetup && T_FUNCTION == $token[0]) { $insideSetup = 0; } elseif (0 == $insideSetup && T_STRING == $token[0]) { $insideSetup = $method == $token[1] ? 1 : -1; } $code .= $token[1]; } else { if (1 == $insideSetup && '{' == $token) { if (!$parens) { $code .= $topCode ? $token.PHP_EOL.' '.$topCode : $token; } else { $code .= $token; } ++$parens; } elseif (1 == $insideSetup && '}' == $token) { --$parens; if (!$parens) { $insideSetup = -1; $code .= $bottomCode ? ' '.$bottomCode.PHP_EOL.' '.$token : $token; } else { $code .= $token; } } else { $code .= $token; } } } return $this->code = $code; } /** * Filters each line of the given method through a callable. * * @param string $method The method name * @param mixed $callable A PHP callable that accepts and returns one line of PHP code */ public function filterMethod($method, $callable) { $line = ''; $code = ''; $insideSetup = -1; $parens = 0; $break = false; $tokens = token_get_all($this->code); for ($i = 0; $i < count($tokens); $i++) { $token = $tokens[$i]; if (is_array($token)) { $line .= $token[1]; if (-1 == $insideSetup && T_FUNCTION == $token[0]) { $insideSetup = 0; } elseif (0 == $insideSetup && T_STRING == $token[0]) { $insideSetup = $method == $token[1] ? 1 : -1; } } else { if (1 == $insideSetup && '{' == $token) { ++$parens; } elseif (1 == $insideSetup && '}' == $token) { --$parens; if (!$parens) { $break = true; } } $line .= $token; } $lines = preg_split('/(\r?\n)/', $line, null, PREG_SPLIT_DELIM_CAPTURE); if (count($lines) > 1 || $break) { $line = $break ? '' : array_pop($lines); foreach (array_chunk($lines, 2) as $chunk) { list($l, $eol) = array_pad($chunk, 2, ''); if (1 == $insideSetup) { list($before, $setup) = $this->splitSetup($l); $code .= $before; $code .= call_user_func($callable, $setup.$eol); } else { $code .= $l.$eol; } } } if ($break) { $insideSetup = -1; $break = false; } } if ($line) { $code .= $line; } return $this->code = $code; } protected function splitSetup($line) { $before = ''; $setup = ''; if ($line) { if (false === stripos($line, 'getTokenValue($token); if (is_array($token) && in_array($token[0], self::$signatureTokens)) { $inSignature = true; } elseif ($inSignature && !preg_match('/\s+/', $value)) { // clean up preg_match('/^\s*/', $setup, $match); $before = implode('', array_map(array($this, 'getTokenValue'), $tokens)).$value.$match[0]; $setup = substr($setup, strlen($match[0])); return array($before, $setup); } $setup = $value.$setup; } } return array($before, $setup); } /** * Returns a token's string value. * * @param array|string $token * * @return string */ protected function getTokenValue($token) { return is_array($token) ? $token[1] : $token; } }