Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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: sfClassManipulator.class.php 25063 2009-12-08 06:02:07Z Kris.Wallsmith $
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
}