Development

/branches/1.4/lib/util/sfClassManipulator.class.php

You must first sign up to be able to contribute.

root/branches/1.4/lib/util/sfClassManipulator.class.php

Revision 25063, 6.6 kB (checked in by Kris.Wallsmith, 5 years ago)

[1.3, 1.4] updated class manipulator to work with mixed eol styles and no eol (closes #7694)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * sfClassManipulator manipulates class code.
13  *
14  * @package    symfony
15  * @subpackage util
16  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
17  * @version    SVN: $Id$
18  */
19 class sfClassManipulator
20 {
21   static protected $signatureTokens = array(
22     T_FINAL,
23     T_ABSTRACT,
24     T_STATIC,
25     T_PUBLIC,
26     T_PROTECTED,
27     T_PRIVATE,
28     T_FUNCTION,
29   );
30
31   protected $code = '', $file = false;
32
33   /**
34    * Constructor.
35    *
36    * @param string $code The code to manipulate
37    */
38   public function __construct($code)
39   {
40     $this->code = $code;
41   }
42
43   /**
44    * Creates a manipulator object from a file.
45    *
46    * @param string $file A file name
47    *
48    * @return sfClassManipulator A sfClassManipulator instance
49    */
50   static public function fromFile($file)
51   {
52     $manipulator = new self(file_get_contents($file));
53     $manipulator->setFile($file);
54
55     return $manipulator;
56   }
57
58   /**
59    * Saves the code back to the associated file.
60    *
61    * This only works if you have bound the instance with a file with the setFile() method.
62    *
63    * @throw LogicException if no file is associated with the instance
64    */
65   public function save()
66   {
67     if (!$this->file)
68     {
69       throw new LogicException('Unable to save the code as no file has been provided.');
70     }
71
72     file_put_contents($this->file, $this->code);
73   }
74
75   /**
76    * Gets the modified code.
77    *
78    * @return string The modified code
79    */
80   public function getCode()
81   {
82     return $this->code;
83   }
84
85   /**
86    * Gets the associated file.
87    *
88    * @return string The associated file
89    */
90   public function getFile()
91   {
92     return $this->file;
93   }
94
95   /**
96    * Sets the file associated with this instance.
97    *
98    * @param string A file name
99    */
100   public function setFile($file)
101   {
102     $this->file = $file;
103   }
104
105   /**
106    * Wraps an existing method with some code.
107    *
108    * @param string $method     The method name to change
109    * @param string $topCode    The code to add at the top of the method
110    * @param string $bottomCode The code to add at the bottom of the method
111    */
112   public function wrapMethod($method, $topCode = '', $bottomCode = '')
113   {
114     $code = '';
115     $insideSetup = -1;
116     $parens = 0;
117     foreach (token_get_all($this->code) as $token)
118     {
119       if (isset($token[1]))
120       {
121         if (-1 == $insideSetup && T_FUNCTION == $token[0])
122         {
123           $insideSetup = 0;
124         }
125         elseif (0 == $insideSetup && T_STRING == $token[0])
126         {
127           $insideSetup = $method == $token[1] ? 1 : -1;
128         }
129
130         $code .= $token[1];
131       }
132       else
133       {
134         if (1 == $insideSetup && '{' == $token)
135         {
136           if (!$parens)
137           {
138             $code .= $topCode ? $token.PHP_EOL.'    '.$topCode : $token;
139           }
140           else
141           {
142             $code .= $token;
143           }
144
145           ++$parens;
146         }
147         elseif (1 == $insideSetup && '}' == $token)
148         {
149           --$parens;
150
151           if (!$parens)
152           {
153             $insideSetup = -1;
154
155             $code .= $bottomCode ? '  '.$bottomCode.PHP_EOL.'  '.$token : $token;
156           }
157           else
158           {
159             $code .= $token;
160           }
161         }
162         else
163         {
164           $code .= $token;
165         }
166       }
167     }
168
169     return $this->code = $code;
170   }
171
172   /**
173    * Filters each line of the given method through a callable.
174    *
175    * @param string $method   The method name
176    * @param mixed  $callable A PHP callable that accepts and returns one line of PHP code
177    */
178   public function filterMethod($method, $callable)
179   {
180     $line = '';
181     $code = '';
182     $insideSetup = -1;
183     $parens = 0;
184     $break = false;
185
186     $tokens = token_get_all($this->code);
187     for ($i = 0; $i < count($tokens); $i++)
188     {
189       $token = $tokens[$i];
190
191       if (is_array($token))
192       {
193         $line .= $token[1];
194
195         if (-1 == $insideSetup && T_FUNCTION == $token[0])
196         {
197           $insideSetup = 0;
198         }
199         elseif (0 == $insideSetup && T_STRING == $token[0])
200         {
201           $insideSetup = $method == $token[1] ? 1 : -1;
202         }
203       }
204       else
205       {
206         if (1 == $insideSetup && '{' == $token)
207         {
208           ++$parens;
209         }
210         elseif (1 == $insideSetup && '}' == $token)
211         {
212           --$parens;
213
214           if (!$parens)
215           {
216             $break = true;
217           }
218         }
219
220         $line .= $token;
221       }
222
223       $lines = preg_split('/(\r?\n)/', $line, null, PREG_SPLIT_DELIM_CAPTURE);
224       if (count($lines) > 1 || $break)
225       {
226         $line = $break ? '' : array_pop($lines);
227         foreach (array_chunk($lines, 2) as $chunk)
228         {
229           list($l, $eol) = array_pad($chunk, 2, '');
230
231           if (1 == $insideSetup)
232           {
233             list($before, $setup) = $this->splitSetup($l);
234             $code .= $before;
235             $code .= call_user_func($callable, $setup.$eol);
236           }
237           else
238           {
239             $code .= $l.$eol;
240           }
241         }
242       }
243
244       if ($break)
245       {
246         $insideSetup = -1;
247         $break = false;
248       }
249     }
250
251     if ($line)
252     {
253       $code .= $line;
254     }
255
256     return $this->code = $code;
257   }
258
259   protected function splitSetup($line)
260   {
261     $before = '';
262     $setup = '';
263
264     if ($line)
265     {
266       if (false === stripos($line, '<?php'))
267       {
268         // add a function so we can accurately slice
269         $tokens = token_get_all('<?php function'.$line);
270         $tokens = array_slice($tokens, 2);
271       }
272       else
273       {
274         $tokens = token_get_all($line);
275       }
276
277       // we're in reverse
278       $inSignature = false;
279       while ($token = array_pop($tokens))
280       {
281         $value = $this->getTokenValue($token);
282         if (is_array($token) && in_array($token[0], self::$signatureTokens))
283         {
284           $inSignature = true;
285         }
286         elseif ($inSignature && !preg_match('/\s+/', $value))
287         {
288           // clean up
289           preg_match('/^\s*/', $setup, $match);
290           $before = implode('', array_map(array($this, 'getTokenValue'), $tokens)).$value.$match[0];
291           $setup = substr($setup, strlen($match[0]));
292
293           return array($before, $setup);
294         }
295
296         $setup = $value.$setup;
297       }
298     }
299
300     return array($before, $setup);
301   }
302
303   /**
304    * Returns a token's string value.
305    *
306    * @param array|string $token
307    *
308    * @return string
309    */
310   protected function getTokenValue($token)
311   {
312     return is_array($token) ? $token[1] : $token;
313   }
314 }
315
Note: See TracBrowser for help on using the browser.