|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_CodeGenerator |
|
17 * @subpackage PHP |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
20 * @version $Id: File.php 20096 2010-01-06 02:05:09Z bkarwin $ |
|
21 */ |
|
22 |
|
23 /** |
|
24 * @see Zend_CodeGenerator_Php_Abstract |
|
25 */ |
|
26 require_once 'Zend/CodeGenerator/Php/Abstract.php'; |
|
27 |
|
28 /** |
|
29 * @see Zend_CodeGenerator_Php_Class |
|
30 */ |
|
31 require_once 'Zend/CodeGenerator/Php/Class.php'; |
|
32 |
|
33 /** |
|
34 * @category Zend |
|
35 * @package Zend_CodeGenerator |
|
36 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
37 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
38 */ |
|
39 class Zend_CodeGenerator_Php_File extends Zend_CodeGenerator_Php_Abstract |
|
40 { |
|
41 |
|
42 /** |
|
43 * @var array Array of Zend_CodeGenerator_Php_File |
|
44 */ |
|
45 protected static $_fileCodeGenerators = array(); |
|
46 |
|
47 /**#@+ |
|
48 * @var string |
|
49 */ |
|
50 protected static $_markerDocblock = '/* Zend_CodeGenerator_Php_File-DocblockMarker */'; |
|
51 protected static $_markerRequire = '/* Zend_CodeGenerator_Php_File-RequireMarker: {?} */'; |
|
52 protected static $_markerClass = '/* Zend_CodeGenerator_Php_File-ClassMarker: {?} */'; |
|
53 /**#@-*/ |
|
54 |
|
55 /** |
|
56 * @var string |
|
57 */ |
|
58 protected $_filename = null; |
|
59 |
|
60 /** |
|
61 * @var Zend_CodeGenerator_Php_Docblock |
|
62 */ |
|
63 protected $_docblock = null; |
|
64 |
|
65 /** |
|
66 * @var array |
|
67 */ |
|
68 protected $_requiredFiles = array(); |
|
69 |
|
70 /** |
|
71 * @var array |
|
72 */ |
|
73 protected $_classes = array(); |
|
74 |
|
75 /** |
|
76 * @var string |
|
77 */ |
|
78 protected $_body = null; |
|
79 |
|
80 public static function registerFileCodeGenerator(Zend_CodeGenerator_Php_File $fileCodeGenerator, $fileName = null) |
|
81 { |
|
82 if ($fileName == null) { |
|
83 $fileName = $fileCodeGenerator->getFilename(); |
|
84 } |
|
85 |
|
86 if ($fileName == '') { |
|
87 require_once 'Zend/CodeGenerator/Php/Exception.php'; |
|
88 throw new Zend_CodeGenerator_Php_Exception('FileName does not exist.'); |
|
89 } |
|
90 |
|
91 // cannot use realpath since the file might not exist, but we do need to have the index |
|
92 // in the same DIRECTORY_SEPARATOR that realpath would use: |
|
93 $fileName = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $fileName); |
|
94 |
|
95 self::$_fileCodeGenerators[$fileName] = $fileCodeGenerator; |
|
96 |
|
97 } |
|
98 |
|
99 /** |
|
100 * fromReflectedFilePath() - use this if you intend on generating code generation objects based on the same file. |
|
101 * This will keep previous changes to the file in tact during the same PHP process |
|
102 * |
|
103 * @param string $filePath |
|
104 * @param bool $usePreviousCodeGeneratorIfItExists |
|
105 * @param bool $includeIfNotAlreadyIncluded |
|
106 * @return Zend_CodeGenerator_Php_File |
|
107 */ |
|
108 public static function fromReflectedFileName($filePath, $usePreviousCodeGeneratorIfItExists = true, $includeIfNotAlreadyIncluded = true) |
|
109 { |
|
110 $realpath = realpath($filePath); |
|
111 |
|
112 if ($realpath === false) { |
|
113 if ( ($realpath = Zend_Reflection_file::findRealpathInIncludePath($filePath)) === false) { |
|
114 require_once 'Zend/CodeGenerator/Php/Exception.php'; |
|
115 throw new Zend_CodeGenerator_Php_Exception('No file for ' . $realpath . ' was found.'); |
|
116 } |
|
117 } |
|
118 |
|
119 if ($usePreviousCodeGeneratorIfItExists && isset(self::$_fileCodeGenerators[$realpath])) { |
|
120 return self::$_fileCodeGenerators[$realpath]; |
|
121 } |
|
122 |
|
123 if ($includeIfNotAlreadyIncluded && !in_array($realpath, get_included_files())) { |
|
124 include $realpath; |
|
125 } |
|
126 |
|
127 $codeGenerator = self::fromReflection(($fileReflector = new Zend_Reflection_File($realpath))); |
|
128 |
|
129 if (!isset(self::$_fileCodeGenerators[$fileReflector->getFileName()])) { |
|
130 self::$_fileCodeGenerators[$fileReflector->getFileName()] = $codeGenerator; |
|
131 } |
|
132 |
|
133 return $codeGenerator; |
|
134 } |
|
135 |
|
136 /** |
|
137 * fromReflection() |
|
138 * |
|
139 * @param Zend_Reflection_File $reflectionFile |
|
140 * @return Zend_CodeGenerator_Php_File |
|
141 */ |
|
142 public static function fromReflection(Zend_Reflection_File $reflectionFile) |
|
143 { |
|
144 $file = new self(); |
|
145 |
|
146 $file->setSourceContent($reflectionFile->getContents()); |
|
147 $file->setSourceDirty(false); |
|
148 |
|
149 $body = $reflectionFile->getContents(); |
|
150 |
|
151 // @todo this whole area needs to be reworked with respect to how body lines are processed |
|
152 foreach ($reflectionFile->getClasses() as $class) { |
|
153 $file->setClass(Zend_CodeGenerator_Php_Class::fromReflection($class)); |
|
154 $classStartLine = $class->getStartLine(true); |
|
155 $classEndLine = $class->getEndLine(); |
|
156 |
|
157 $bodyLines = explode("\n", $body); |
|
158 $bodyReturn = array(); |
|
159 for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) { |
|
160 if ($lineNum == $classStartLine) { |
|
161 $bodyReturn[] = str_replace('?', $class->getName(), self::$_markerClass); //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */'; |
|
162 $lineNum = $classEndLine; |
|
163 } else { |
|
164 $bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion |
|
165 } |
|
166 } |
|
167 $body = implode("\n", $bodyReturn); |
|
168 unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine); |
|
169 } |
|
170 |
|
171 if (($reflectionFile->getDocComment() != '')) { |
|
172 $docblock = $reflectionFile->getDocblock(); |
|
173 $file->setDocblock(Zend_CodeGenerator_Php_Docblock::fromReflection($docblock)); |
|
174 |
|
175 $bodyLines = explode("\n", $body); |
|
176 $bodyReturn = array(); |
|
177 for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) { |
|
178 if ($lineNum == $docblock->getStartLine()) { |
|
179 $bodyReturn[] = str_replace('?', $class->getName(), self::$_markerDocblock); //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */'; |
|
180 $lineNum = $docblock->getEndLine(); |
|
181 } else { |
|
182 $bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion |
|
183 } |
|
184 } |
|
185 $body = implode("\n", $bodyReturn); |
|
186 unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine); |
|
187 } |
|
188 |
|
189 $file->setBody($body); |
|
190 |
|
191 return $file; |
|
192 } |
|
193 |
|
194 /** |
|
195 * setDocblock() Set the docblock |
|
196 * |
|
197 * @param Zend_CodeGenerator_Php_Docblock|array|string $docblock |
|
198 * @return Zend_CodeGenerator_Php_File |
|
199 */ |
|
200 public function setDocblock($docblock) |
|
201 { |
|
202 if (is_string($docblock)) { |
|
203 $docblock = array('shortDescription' => $docblock); |
|
204 } |
|
205 |
|
206 if (is_array($docblock)) { |
|
207 $docblock = new Zend_CodeGenerator_Php_Docblock($docblock); |
|
208 } elseif (!$docblock instanceof Zend_CodeGenerator_Php_Docblock) { |
|
209 require_once 'Zend/CodeGenerator/Php/Exception.php'; |
|
210 throw new Zend_CodeGenerator_Php_Exception('setDocblock() is expecting either a string, array or an instance of Zend_CodeGenerator_Php_Docblock'); |
|
211 } |
|
212 |
|
213 $this->_docblock = $docblock; |
|
214 return $this; |
|
215 } |
|
216 |
|
217 /** |
|
218 * Get docblock |
|
219 * |
|
220 * @return Zend_CodeGenerator_Php_Docblock |
|
221 */ |
|
222 public function getDocblock() |
|
223 { |
|
224 return $this->_docblock; |
|
225 } |
|
226 |
|
227 /** |
|
228 * setRequiredFiles |
|
229 * |
|
230 * @param array $requiredFiles |
|
231 * @return Zend_CodeGenerator_Php_File |
|
232 */ |
|
233 public function setRequiredFiles($requiredFiles) |
|
234 { |
|
235 $this->_requiredFiles = $requiredFiles; |
|
236 return $this; |
|
237 } |
|
238 |
|
239 /** |
|
240 * getRequiredFiles() |
|
241 * |
|
242 * @return array |
|
243 */ |
|
244 public function getRequiredFiles() |
|
245 { |
|
246 return $this->_requiredFiles; |
|
247 } |
|
248 |
|
249 /** |
|
250 * setClasses() |
|
251 * |
|
252 * @param array $classes |
|
253 * @return Zend_CodeGenerator_Php_File |
|
254 */ |
|
255 public function setClasses(Array $classes) |
|
256 { |
|
257 foreach ($classes as $class) { |
|
258 $this->setClass($class); |
|
259 } |
|
260 return $this; |
|
261 } |
|
262 |
|
263 /** |
|
264 * getClass() |
|
265 * |
|
266 * @param string $name |
|
267 * @return Zend_CodeGenerator_Php_Class |
|
268 */ |
|
269 public function getClass($name = null) |
|
270 { |
|
271 if ($name == null) { |
|
272 reset($this->_classes); |
|
273 return current($this->_classes); |
|
274 } |
|
275 |
|
276 return $this->_classes[$name]; |
|
277 } |
|
278 |
|
279 /** |
|
280 * setClass() |
|
281 * |
|
282 * @param Zend_CodeGenerator_Php_Class|array $class |
|
283 * @return Zend_CodeGenerator_Php_File |
|
284 */ |
|
285 public function setClass($class) |
|
286 { |
|
287 if (is_array($class)) { |
|
288 $class = new Zend_CodeGenerator_Php_Class($class); |
|
289 $className = $class->getName(); |
|
290 } elseif ($class instanceof Zend_CodeGenerator_Php_Class) { |
|
291 $className = $class->getName(); |
|
292 } else { |
|
293 require_once 'Zend/CodeGenerator/Php/Exception.php'; |
|
294 throw new Zend_CodeGenerator_Php_Exception('Expecting either an array or an instance of Zend_CodeGenerator_Php_Class'); |
|
295 } |
|
296 |
|
297 // @todo check for dup here |
|
298 |
|
299 $this->_classes[$className] = $class; |
|
300 return $this; |
|
301 } |
|
302 |
|
303 /** |
|
304 * setFilename() |
|
305 * |
|
306 * @param string $filename |
|
307 * @return Zend_CodeGenerator_Php_File |
|
308 */ |
|
309 public function setFilename($filename) |
|
310 { |
|
311 $this->_filename = $filename; |
|
312 return $this; |
|
313 } |
|
314 |
|
315 /** |
|
316 * getFilename() |
|
317 * |
|
318 * @return string |
|
319 */ |
|
320 public function getFilename() |
|
321 { |
|
322 return $this->_filename; |
|
323 } |
|
324 |
|
325 /** |
|
326 * getClasses() |
|
327 * |
|
328 * @return array Array of Zend_CodeGenerator_Php_Class |
|
329 */ |
|
330 public function getClasses() |
|
331 { |
|
332 return $this->_classes; |
|
333 } |
|
334 |
|
335 /** |
|
336 * setBody() |
|
337 * |
|
338 * @param string $body |
|
339 * @return Zend_CodeGenerator_Php_File |
|
340 */ |
|
341 public function setBody($body) |
|
342 { |
|
343 $this->_body = $body; |
|
344 return $this; |
|
345 } |
|
346 |
|
347 /** |
|
348 * getBody() |
|
349 * |
|
350 * @return string |
|
351 */ |
|
352 public function getBody() |
|
353 { |
|
354 return $this->_body; |
|
355 } |
|
356 |
|
357 /** |
|
358 * isSourceDirty() |
|
359 * |
|
360 * @return bool |
|
361 */ |
|
362 public function isSourceDirty() |
|
363 { |
|
364 if (($docblock = $this->getDocblock()) && $docblock->isSourceDirty()) { |
|
365 return true; |
|
366 } |
|
367 |
|
368 foreach ($this->_classes as $class) { |
|
369 if ($class->isSourceDirty()) { |
|
370 return true; |
|
371 } |
|
372 } |
|
373 |
|
374 return parent::isSourceDirty(); |
|
375 } |
|
376 |
|
377 /** |
|
378 * generate() |
|
379 * |
|
380 * @return string |
|
381 */ |
|
382 public function generate() |
|
383 { |
|
384 if ($this->isSourceDirty() === false) { |
|
385 return $this->_sourceContent; |
|
386 } |
|
387 |
|
388 $output = ''; |
|
389 |
|
390 // start with the body (if there), or open tag |
|
391 if (preg_match('#(?:\s*)<\?php#', $this->getBody()) == false) { |
|
392 $output = '<?php' . self::LINE_FEED; |
|
393 } |
|
394 |
|
395 // if there are markers, put the body into the output |
|
396 $body = $this->getBody(); |
|
397 if (preg_match('#/\* Zend_CodeGenerator_Php_File-(.*?)Marker:#', $body)) { |
|
398 $output .= $body; |
|
399 $body = ''; |
|
400 } |
|
401 |
|
402 // Add file docblock, if any |
|
403 if (null !== ($docblock = $this->getDocblock())) { |
|
404 $docblock->setIndentation(''); |
|
405 $regex = preg_quote(self::$_markerDocblock, '#'); |
|
406 if (preg_match('#'.$regex.'#', $output)) { |
|
407 $output = preg_replace('#'.$regex.'#', $docblock->generate(), $output, 1); |
|
408 } else { |
|
409 $output .= $docblock->generate() . self::LINE_FEED; |
|
410 } |
|
411 } |
|
412 |
|
413 // newline |
|
414 $output .= self::LINE_FEED; |
|
415 |
|
416 // process required files |
|
417 // @todo marker replacement for required files |
|
418 $requiredFiles = $this->getRequiredFiles(); |
|
419 if (!empty($requiredFiles)) { |
|
420 foreach ($requiredFiles as $requiredFile) { |
|
421 $output .= 'require_once \'' . $requiredFile . '\';' . self::LINE_FEED; |
|
422 } |
|
423 |
|
424 $output .= self::LINE_FEED; |
|
425 } |
|
426 |
|
427 // process classes |
|
428 $classes = $this->getClasses(); |
|
429 if (!empty($classes)) { |
|
430 foreach ($classes as $class) { |
|
431 $regex = str_replace('?', $class->getName(), self::$_markerClass); |
|
432 $regex = preg_quote($regex, '#'); |
|
433 if (preg_match('#'.$regex.'#', $output)) { |
|
434 $output = preg_replace('#'.$regex.'#', $class->generate(), $output, 1); |
|
435 } else { |
|
436 $output .= $class->generate() . self::LINE_FEED; |
|
437 } |
|
438 } |
|
439 |
|
440 } |
|
441 |
|
442 if (!empty($body)) { |
|
443 |
|
444 // add an extra space betwee clsses and |
|
445 if (!empty($classes)) { |
|
446 $output .= self::LINE_FEED; |
|
447 } |
|
448 |
|
449 $output .= $body; |
|
450 } |
|
451 |
|
452 return $output; |
|
453 } |
|
454 |
|
455 public function write() |
|
456 { |
|
457 if ($this->_filename == '' || !is_writable(dirname($this->_filename))) { |
|
458 require_once 'Zend/CodeGenerator/Php/Exception.php'; |
|
459 throw new Zend_CodeGenerator_Php_Exception('This code generator object is not writable.'); |
|
460 } |
|
461 file_put_contents($this->_filename, $this->generate()); |
|
462 return $this; |
|
463 } |
|
464 |
|
465 } |