Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
12345 anikendra 1
<?php
2
/**
3
 * Upgrade Shell
4
 *
5
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
6
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
7
 *
8
 * Licensed under The MIT License
9
 * For full copyright and license information, please see the LICENSE.txt
10
 * Redistributions of files must retain the above copyright notice.
11
 *
12
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
13
 * @link          http://cakephp.org CakePHP(tm) Project
14
 * @package       Cake.Console.Command
15
 * @since         CakePHP(tm) v 2.0
16
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
17
 */
18
 
19
App::uses('AppShell', 'Console/Command');
20
App::uses('Folder', 'Utility');
21
App::uses('CakePlugin', 'Core');
22
 
23
/**
24
 * A shell class to help developers upgrade applications to CakePHP 2.0
25
 *
26
 * @package       Cake.Console.Command
27
 */
28
class UpgradeShell extends AppShell {
29
 
30
/**
31
 * Files
32
 *
33
 * @var array
34
 */
35
	protected $_files = array();
36
 
37
/**
38
 * Paths
39
 *
40
 * @var array
41
 */
42
	protected $_paths = array();
43
 
44
/**
45
 * Map
46
 *
47
 * @var array
48
 */
49
	protected $_map = array(
50
		'Controller' => 'Controller',
51
		'Component' => 'Controller/Component',
52
		'Model' => 'Model',
53
		'Behavior' => 'Model/Behavior',
54
		'Datasource' => 'Model/Datasource',
55
		'Dbo' => 'Model/Datasource/Database',
56
		'View' => 'View',
57
		'Helper' => 'View/Helper',
58
		'Shell' => 'Console/Command',
59
		'Task' => 'Console/Command/Task',
60
		'Case' => 'Test/Case',
61
		'Fixture' => 'Test/Fixture',
62
		'Error' => 'Lib/Error',
63
	);
64
 
65
/**
66
 * Shell startup, prints info message about dry run.
67
 *
68
 * @return void
69
 */
70
	public function startup() {
71
		parent::startup();
72
		if ($this->params['dry-run']) {
73
			$this->out(__d('cake_console', '<warning>Dry-run mode enabled!</warning>'), 1, Shell::QUIET);
74
		}
75
		if ($this->params['git'] && !is_dir('.git')) {
76
			$this->out(__d('cake_console', '<warning>No git repository detected!</warning>'), 1, Shell::QUIET);
77
		}
78
	}
79
 
80
/**
81
 * Run all upgrade steps one at a time
82
 *
83
 * @return void
84
 */
85
	public function all() {
86
		foreach ($this->OptionParser->subcommands() as $command) {
87
			$name = $command->name();
88
			if ($name === 'all') {
89
				continue;
90
			}
91
			$this->out(__d('cake_console', 'Running %s', $name));
92
			$this->$name();
93
		}
94
	}
95
 
96
/**
97
 * Update tests.
98
 *
99
 * - Update tests class names to FooTest rather than FooTestCase.
100
 *
101
 * @return void
102
 */
103
	public function tests() {
104
		$this->_paths = array(APP . 'tests' . DS);
105
		if (!empty($this->params['plugin'])) {
106
			$this->_paths = array(CakePlugin::path($this->params['plugin']) . 'tests' . DS);
107
		}
108
		$patterns = array(
109
			array(
110
				'*TestCase extends CakeTestCase to *Test extends CakeTestCase',
111
				'/([a-zA-Z]*Test)Case extends CakeTestCase/',
112
				'\1 extends CakeTestCase'
113
			),
114
		);
115
 
116
		$this->_filesRegexpUpdate($patterns);
117
	}
118
 
119
/**
120
 * Move files and folders to their new homes
121
 *
122
 * Moves folders containing files which cannot necessarily be auto-detected (libs and templates)
123
 * and then looks for all php files except vendors, and moves them to where Cake 2.0 expects
124
 * to find them.
125
 *
126
 * @return void
127
 */
128
	public function locations() {
129
		$cwd = getcwd();
130
 
131
		if (!empty($this->params['plugin'])) {
132
			chdir(CakePlugin::path($this->params['plugin']));
133
		}
134
 
135
		if (is_dir('plugins')) {
136
			$Folder = new Folder('plugins');
137
			list($plugins) = $Folder->read();
138
			foreach ($plugins as $plugin) {
139
				chdir($cwd . DS . 'plugins' . DS . $plugin);
140
				$this->out(__d('cake_console', 'Upgrading locations for plugin %s', $plugin));
141
				$this->locations();
142
			}
143
			$this->_files = array();
144
			chdir($cwd);
145
			$this->out(__d('cake_console', 'Upgrading locations for app directory'));
146
		}
147
		$moves = array(
148
			'config' => 'Config',
149
			'Config' . DS . 'schema' => 'Config' . DS . 'Schema',
150
			'libs' => 'Lib',
151
			'tests' => 'Test',
152
			'views' => 'View',
153
			'models' => 'Model',
154
			'Model' . DS . 'behaviors' => 'Model' . DS . 'Behavior',
155
			'Model' . DS . 'datasources' => 'Model' . DS . 'Datasource',
156
			'Test' . DS . 'cases' => 'Test' . DS . 'Case',
157
			'Test' . DS . 'fixtures' => 'Test' . DS . 'Fixture',
158
			'vendors' . DS . 'shells' . DS . 'templates' => 'Console' . DS . 'Templates',
159
		);
160
		foreach ($moves as $old => $new) {
161
			if (is_dir($old)) {
162
				$this->out(__d('cake_console', 'Moving %s to %s', $old, $new));
163
				if (!$this->params['dry-run']) {
164
					if ($this->params['git']) {
165
						exec('git mv -f ' . escapeshellarg($old) . ' ' . escapeshellarg($old . '__'));
166
						exec('git mv -f ' . escapeshellarg($old . '__') . ' ' . escapeshellarg($new));
167
					} else {
168
						$Folder = new Folder($old);
169
						$Folder->move($new);
170
					}
171
				}
172
			}
173
		}
174
 
175
		$this->_moveViewFiles();
176
		$this->_moveAppClasses();
177
 
178
		$sourceDirs = array(
179
			'.' => array('recursive' => false),
180
			'Console',
181
			'controllers',
182
			'Controller',
183
			'Lib' => array('checkFolder' => false),
184
			'models',
185
			'Model',
186
			'tests',
187
			'Test' => array('regex' => '@class (\S*Test) extends CakeTestCase@'),
188
			'views',
189
			'View',
190
			'vendors/shells',
191
		);
192
 
193
		$defaultOptions = array(
194
			'recursive' => true,
195
			'checkFolder' => true,
196
			'regex' => '@class (\S*) .*(\s|\v)*{@i'
197
		);
198
		foreach ($sourceDirs as $dir => $options) {
199
			if (is_numeric($dir)) {
200
				$dir = $options;
201
				$options = array();
202
			}
203
			$options += $defaultOptions;
204
			$this->_movePhpFiles($dir, $options);
205
		}
206
	}
207
 
208
/**
209
 * Update helpers.
210
 *
211
 * - Converts helpers usage to new format.
212
 *
213
 * @return void
214
 */
215
	public function helpers() {
216
		$this->_paths = array_diff(App::path('views'), App::core('views'));
217
 
218
		if (!empty($this->params['plugin'])) {
219
			$this->_paths = array(CakePlugin::path($this->params['plugin']) . 'views' . DS);
220
		}
221
 
222
		$patterns = array();
223
		App::build(array(
224
			'View/Helper' => App::core('View/Helper'),
225
		), App::APPEND);
226
		$helpers = App::objects('helper');
227
		$plugins = App::objects('plugin');
228
		$pluginHelpers = array();
229
		foreach ($plugins as $plugin) {
230
			CakePlugin::load($plugin);
231
			$pluginHelpers = array_merge(
232
				$pluginHelpers,
233
				App::objects('helper', CakePlugin::path($plugin) . DS . 'views' . DS . 'helpers' . DS, false)
234
			);
235
		}
236
		$helpers = array_merge($pluginHelpers, $helpers);
237
		foreach ($helpers as $helper) {
238
			$helper = preg_replace('/Helper$/', '', $helper);
239
			$oldHelper = $helper;
240
			$oldHelper{0} = strtolower($oldHelper{0});
241
			$patterns[] = array(
242
				"\${$oldHelper} to \$this->{$helper}",
243
				"/\\\${$oldHelper}->/",
244
				"\\\$this->{$helper}->"
245
			);
246
		}
247
 
248
		$this->_filesRegexpUpdate($patterns);
249
	}
250
 
251
/**
252
 * Update i18n.
253
 *
254
 * - Removes extra true param.
255
 * - Add the echo to __*() calls that didn't need them before.
256
 *
257
 * @return void
258
 */
259
	public function i18n() {
260
		$this->_paths = array(
261
			APP
262
		);
263
		if (!empty($this->params['plugin'])) {
264
			$this->_paths = array(CakePlugin::path($this->params['plugin']));
265
		}
266
 
267
		$patterns = array(
268
			array(
269
				'<?php __*(*) to <?php echo __*(*)',
270
				'/<\?php\s*(__[a-z]*\(.*?\))/',
271
				'<?php echo \1'
272
			),
273
			array(
274
				'<?php __*(*, true) to <?php echo __*()',
275
				'/<\?php\s*(__[a-z]*\(.*?)(,\s*true)(\))/',
276
				'<?php echo \1\3'
277
			),
278
			array('__*(*, true) to __*(*)', '/(__[a-z]*\(.*?)(,\s*true)(\))/', '\1\3')
279
		);
280
 
281
		$this->_filesRegexpUpdate($patterns);
282
	}
283
 
284
/**
285
 * Upgrade the removed basics functions.
286
 *
287
 * - a(*) -> array(*)
288
 * - e(*) -> echo *
289
 * - ife(*, *, *) -> !empty(*) ? * : *
290
 * - a(*) -> array(*)
291
 * - r(*, *, *) -> str_replace(*, *, *)
292
 * - up(*) -> strtoupper(*)
293
 * - low(*, *, *) -> strtolower(*)
294
 * - getMicrotime() -> microtime(true)
295
 *
296
 * @return void
297
 */
298
	public function basics() {
299
		$this->_paths = array(
300
			APP
301
		);
302
		if (!empty($this->params['plugin'])) {
303
			$this->_paths = array(CakePlugin::path($this->params['plugin']));
304
		}
305
		$patterns = array(
306
			array(
307
				'a(*) -> array(*)',
308
				'/\ba\((.*)\)/',
309
				'array(\1)'
310
			),
311
			array(
312
				'e(*) -> echo *',
313
				'/\be\((.*)\)/',
314
				'echo \1'
315
			),
316
			array(
317
				'ife(*, *, *) -> !empty(*) ? * : *',
318
				'/ife\((.*), (.*), (.*)\)/',
319
				'!empty(\1) ? \2 : \3'
320
			),
321
			array(
322
				'r(*, *, *) -> str_replace(*, *, *)',
323
				'/\br\(/',
324
				'str_replace('
325
			),
326
			array(
327
				'up(*) -> strtoupper(*)',
328
				'/\bup\(/',
329
				'strtoupper('
330
			),
331
			array(
332
				'low(*) -> strtolower(*)',
333
				'/\blow\(/',
334
				'strtolower('
335
			),
336
			array(
337
				'getMicrotime() -> microtime(true)',
338
				'/getMicrotime\(\)/',
339
				'microtime(true)'
340
			),
341
		);
342
		$this->_filesRegexpUpdate($patterns);
343
	}
344
 
345
/**
346
 * Update the properties moved to CakeRequest.
347
 *
348
 * @return void
349
 */
350
	public function request() {
351
		$views = array_diff(App::path('views'), App::core('views'));
352
		$controllers = array_diff(App::path('controllers'), App::core('controllers'), array(APP));
353
		$components = array_diff(App::path('components'), App::core('components'));
354
 
355
		$this->_paths = array_merge($views, $controllers, $components);
356
 
357
		if (!empty($this->params['plugin'])) {
358
			$pluginPath = CakePlugin::path($this->params['plugin']);
359
			$this->_paths = array(
360
				$pluginPath . 'controllers' . DS,
361
				$pluginPath . 'controllers' . DS . 'components' . DS,
362
				$pluginPath . 'views' . DS,
363
			);
364
		}
365
		$patterns = array(
366
			array(
367
				'$this->data -> $this->request->data',
368
				'/(\$this->data\b(?!\())/',
369
				'$this->request->data'
370
			),
371
			array(
372
				'$this->params -> $this->request->params',
373
				'/(\$this->params\b(?!\())/',
374
				'$this->request->params'
375
			),
376
			array(
377
				'$this->webroot -> $this->request->webroot',
378
				'/(\$this->webroot\b(?!\())/',
379
				'$this->request->webroot'
380
			),
381
			array(
382
				'$this->base -> $this->request->base',
383
				'/(\$this->base\b(?!\())/',
384
				'$this->request->base'
385
			),
386
			array(
387
				'$this->here -> $this->request->here',
388
				'/(\$this->here\b(?!\())/',
389
				'$this->request->here'
390
			),
391
			array(
392
				'$this->action -> $this->request->action',
393
				'/(\$this->action\b(?!\())/',
394
				'$this->request->action'
395
			),
396
			array(
397
				'$this->request->onlyAllow() -> $this->request->allowMethod()',
398
				'/\$this->request->onlyAllow\(/',
399
				'$this->request->allowMethod('
400
			)
401
		);
402
		$this->_filesRegexpUpdate($patterns);
403
	}
404
 
405
/**
406
 * Update Configure::read() calls with no params.
407
 *
408
 * @return void
409
 */
410
	public function configure() {
411
		$this->_paths = array(
412
			APP
413
		);
414
		if (!empty($this->params['plugin'])) {
415
			$this->_paths = array(CakePlugin::path($this->params['plugin']));
416
		}
417
		$patterns = array(
418
			array(
419
				"Configure::read() -> Configure::read('debug')",
420
				'/Configure::read\(\)/',
421
				'Configure::read(\'debug\')'
422
			),
423
		);
424
		$this->_filesRegexpUpdate($patterns);
425
	}
426
 
427
/**
428
 * constants
429
 *
430
 * @return void
431
 */
432
	public function constants() {
433
		$this->_paths = array(
434
			APP
435
		);
436
		if (!empty($this->params['plugin'])) {
437
			$this->_paths = array(CakePlugin::path($this->params['plugin']));
438
		}
439
		$patterns = array(
440
			array(
441
				"LIBS -> CAKE",
442
				'/\bLIBS\b/',
443
				'CAKE'
444
			),
445
			array(
446
				"CONFIGS -> APP . 'Config' . DS",
447
				'/\bCONFIGS\b/',
448
				'APP . \'Config\' . DS'
449
			),
450
			array(
451
				"CONTROLLERS -> APP . 'Controller' . DS",
452
				'/\bCONTROLLERS\b/',
453
				'APP . \'Controller\' . DS'
454
			),
455
			array(
456
				"COMPONENTS -> APP . 'Controller' . DS . 'Component' . DS",
457
				'/\bCOMPONENTS\b/',
458
				'APP . \'Controller\' . DS . \'Component\''
459
			),
460
			array(
461
				"MODELS -> APP . 'Model' . DS",
462
				'/\bMODELS\b/',
463
				'APP . \'Model\' . DS'
464
			),
465
			array(
466
				"BEHAVIORS -> APP . 'Model' . DS . 'Behavior' . DS",
467
				'/\bBEHAVIORS\b/',
468
				'APP . \'Model\' . DS . \'Behavior\' . DS'
469
			),
470
			array(
471
				"VIEWS -> APP . 'View' . DS",
472
				'/\bVIEWS\b/',
473
				'APP . \'View\' . DS'
474
			),
475
			array(
476
				"HELPERS -> APP . 'View' . DS . 'Helper' . DS",
477
				'/\bHELPERS\b/',
478
				'APP . \'View\' . DS . \'Helper\' . DS'
479
			),
480
			array(
481
				"LAYOUTS -> APP . 'View' . DS . 'Layouts' . DS",
482
				'/\bLAYOUTS\b/',
483
				'APP . \'View\' . DS . \'Layouts\' . DS'
484
			),
485
			array(
486
				"ELEMENTS -> APP . 'View' . DS . 'Elements' . DS",
487
				'/\bELEMENTS\b/',
488
				'APP . \'View\' . DS . \'Elements\' . DS'
489
			),
490
			array(
491
				"CONSOLE_LIBS -> CAKE . 'Console' . DS",
492
				'/\bCONSOLE_LIBS\b/',
493
				'CAKE . \'Console\' . DS'
494
			),
495
			array(
496
				"CAKE_TESTS_LIB -> CAKE . 'TestSuite' . DS",
497
				'/\bCAKE_TESTS_LIB\b/',
498
				'CAKE . \'TestSuite\' . DS'
499
			),
500
			array(
501
				"CAKE_TESTS -> CAKE . 'Test' . DS",
502
				'/\bCAKE_TESTS\b/',
503
				'CAKE . \'Test\' . DS'
504
			)
505
		);
506
		$this->_filesRegexpUpdate($patterns);
507
	}
508
 
509
/**
510
 * Update controller redirects.
511
 *
512
 * - Make redirect statements return early.
513
 *
514
 * @return void
515
 */
516
	public function controller_redirects() {
517
		$this->_paths = App::Path('Controller');
518
		if (!empty($this->params['plugin'])) {
519
			$this->_paths = App::Path('Controller', $this->params['plugin']);
520
		}
521
		$patterns = array(
522
			array(
523
				'$this->redirect() to return $this->redirect()',
524
				'/\t\$this-\>redirect\(/',
525
				"\t" . 'return $this->redirect('
526
			),
527
		);
528
 
529
		$this->_filesRegexpUpdate($patterns);
530
	}
531
 
532
/**
533
 * Update components.
534
 *
535
 * - Make components that extend Object to extend Component.
536
 *
537
 * @return void
538
 */
539
	public function components() {
540
		$this->_paths = App::Path('Controller/Component');
541
		if (!empty($this->params['plugin'])) {
542
			$this->_paths = App::Path('Controller/Component', $this->params['plugin']);
543
		}
544
		$patterns = array(
545
			array(
546
				'*Component extends Object to *Component extends Component',
547
				'/([a-zA-Z]*Component extends) Object/',
548
				'\1 Component'
549
			),
550
		);
551
 
552
		$this->_filesRegexpUpdate($patterns);
553
	}
554
 
555
/**
556
 * Replace cakeError with built-in exceptions.
557
 * NOTE: this ignores calls where you've passed your own secondary parameters to cakeError().
558
 *
559
 * @return void
560
 */
561
	public function exceptions() {
562
		$controllers = array_diff(App::path('controllers'), App::core('controllers'), array(APP));
563
		$components = array_diff(App::path('components'), App::core('components'));
564
 
565
		$this->_paths = array_merge($controllers, $components);
566
 
567
		if (!empty($this->params['plugin'])) {
568
			$pluginPath = CakePlugin::path($this->params['plugin']);
569
			$this->_paths = array(
570
				$pluginPath . 'controllers' . DS,
571
				$pluginPath . 'controllers' . DS . 'components' . DS,
572
			);
573
		}
574
		$patterns = array(
575
			array(
576
				'$this->cakeError("error400") -> throw new BadRequestException()',
577
				'/(\$this->cakeError\(["\']error400["\']\));/',
578
				'throw new BadRequestException();'
579
			),
580
			array(
581
				'$this->cakeError("error404") -> throw new NotFoundException()',
582
				'/(\$this->cakeError\(["\']error404["\']\));/',
583
				'throw new NotFoundException();'
584
			),
585
			array(
586
				'$this->cakeError("error500") -> throw new InternalErrorException()',
587
				'/(\$this->cakeError\(["\']error500["\']\));/',
588
				'throw new InternalErrorException();'
589
			),
590
		);
591
		$this->_filesRegexpUpdate($patterns);
592
	}
593
 
594
/**
595
 * Move application views files to where they now should be
596
 *
597
 * Find all view files in the folder and determine where cake expects the file to be
598
 *
599
 * @return void
600
 */
601
	protected function _moveViewFiles() {
602
		if (!is_dir('View')) {
603
			return;
604
		}
605
 
606
		$dirs = scandir('View');
607
		foreach ($dirs as $old) {
608
			if (!is_dir('View' . DS . $old) || $old === '.' || $old === '..') {
609
				continue;
610
			}
611
 
612
			$new = 'View' . DS . Inflector::camelize($old);
613
			$old = 'View' . DS . $old;
614
			if ($new === $old) {
615
				continue;
616
			}
617
 
618
			$this->out(__d('cake_console', 'Moving %s to %s', $old, $new));
619
			if (!$this->params['dry-run']) {
620
				if ($this->params['git']) {
621
					exec('git mv -f ' . escapeshellarg($old) . ' ' . escapeshellarg($old . '__'));
622
					exec('git mv -f ' . escapeshellarg($old . '__') . ' ' . escapeshellarg($new));
623
				} else {
624
					$Folder = new Folder($old);
625
					$Folder->move($new);
626
				}
627
			}
628
		}
629
	}
630
 
631
/**
632
 * Move the AppController, and AppModel classes.
633
 *
634
 * @return void
635
 */
636
	protected function _moveAppClasses() {
637
		$files = array(
638
			APP . 'app_controller.php' => APP . 'Controller' . DS . 'AppController.php',
639
			APP . 'controllers' . DS . 'app_controller.php' => APP . 'Controller' . DS . 'AppController.php',
640
			APP . 'app_model.php' => APP . 'Model' . DS . 'AppModel.php',
641
			APP . 'models' . DS . 'app_model.php' => APP . 'Model' . DS . 'AppModel.php',
642
		);
643
		foreach ($files as $old => $new) {
644
			if (file_exists($old)) {
645
				$this->out(__d('cake_console', 'Moving %s to %s', $old, $new));
646
 
647
				if ($this->params['dry-run']) {
648
					continue;
649
				}
650
				if ($this->params['git']) {
651
					exec('git mv -f ' . escapeshellarg($old) . ' ' . escapeshellarg($old . '__'));
652
					exec('git mv -f ' . escapeshellarg($old . '__') . ' ' . escapeshellarg($new));
653
				} else {
654
					rename($old, $new);
655
				}
656
			}
657
		}
658
	}
659
 
660
/**
661
 * Move application php files to where they now should be
662
 *
663
 * Find all php files in the folder (honoring recursive) and determine where CakePHP expects the file to be
664
 * If the file is not exactly where CakePHP expects it - move it.
665
 *
666
 * @param string $path The path to move files in.
667
 * @param array $options array(recursive, checkFolder)
668
 * @return void
669
 */
670
	protected function _movePhpFiles($path, $options) {
671
		if (!is_dir($path)) {
672
			return;
673
		}
674
 
675
		$paths = $this->_paths;
676
 
677
		$this->_paths = array($path);
678
		$this->_files = array();
679
		if ($options['recursive']) {
680
			$this->_findFiles('php');
681
		} else {
682
			$this->_files = scandir($path);
683
			foreach ($this->_files as $i => $file) {
684
				if (strlen($file) < 5 || substr($file, -4) !== '.php') {
685
					unset($this->_files[$i]);
686
				}
687
			}
688
		}
689
 
690
		$cwd = getcwd();
691
		foreach ($this->_files as &$file) {
692
			$file = $cwd . DS . $file;
693
 
694
			$contents = file_get_contents($file);
695
			preg_match($options['regex'], $contents, $match);
696
			if (!$match) {
697
				continue;
698
			}
699
 
700
			$class = $match[1];
701
 
702
			if (substr($class, 0, 3) === 'Dbo') {
703
				$type = 'Dbo';
704
			} else {
705
				preg_match('@([A-Z][^A-Z]*)$@', $class, $match);
706
				if ($match) {
707
					$type = $match[1];
708
				} else {
709
					$type = 'unknown';
710
				}
711
			}
712
 
713
			preg_match('@^.*[\\\/]plugins[\\\/](.*?)[\\\/]@', $file, $match);
714
			$base = $cwd . DS;
715
			$plugin = false;
716
			if ($match) {
717
				$base = $match[0];
718
				$plugin = $match[1];
719
			}
720
 
721
			if ($options['checkFolder'] && !empty($this->_map[$type])) {
722
				$folder = str_replace('/', DS, $this->_map[$type]);
723
				$new = $base . $folder . DS . $class . '.php';
724
			} else {
725
				$new = dirname($file) . DS . $class . '.php';
726
			}
727
 
728
			if ($file === $new) {
729
				continue;
730
			}
731
 
732
			$dir = dirname($new);
733
			if (!is_dir($dir)) {
734
				new Folder($dir, true);
735
			}
736
 
737
			$this->out(__d('cake_console', 'Moving %s to %s', $file, $new), 1, Shell::VERBOSE);
738
			if (!$this->params['dry-run']) {
739
				if ($this->params['git']) {
740
					exec('git mv -f ' . escapeshellarg($file) . ' ' . escapeshellarg($file . '__'));
741
					exec('git mv -f ' . escapeshellarg($file . '__') . ' ' . escapeshellarg($new));
742
				} else {
743
					rename($file, $new);
744
				}
745
			}
746
		}
747
 
748
		$this->_paths = $paths;
749
	}
750
 
751
/**
752
 * Updates files based on regular expressions.
753
 *
754
 * @param array $patterns Array of search and replacement patterns.
755
 * @return void
756
 */
757
	protected function _filesRegexpUpdate($patterns) {
758
		$this->_findFiles($this->params['ext']);
759
		foreach ($this->_files as $file) {
760
			$this->out(__d('cake_console', 'Updating %s...', $file), 1, Shell::VERBOSE);
761
			$this->_updateFile($file, $patterns);
762
		}
763
	}
764
 
765
/**
766
 * Searches the paths and finds files based on extension.
767
 *
768
 * @param string $extensions The extensions to include. Defaults to none.
769
 * @return void
770
 */
771
	protected function _findFiles($extensions = '') {
772
		$this->_files = array();
773
		foreach ($this->_paths as $path) {
774
			if (!is_dir($path)) {
775
				continue;
776
			}
777
			$Iterator = new RegexIterator(
778
				new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)),
779
				'/^.+\.(' . $extensions . ')$/i',
780
				RegexIterator::MATCH
781
			);
782
			foreach ($Iterator as $file) {
783
				if ($file->isFile()) {
784
					$this->_files[] = $file->getPathname();
785
				}
786
			}
787
		}
788
	}
789
 
790
/**
791
 * Update a single file.
792
 *
793
 * @param string $file The file to update
794
 * @param array $patterns The replacement patterns to run.
795
 * @return void
796
 */
797
	protected function _updateFile($file, $patterns) {
798
		$contents = file_get_contents($file);
799
 
800
		foreach ($patterns as $pattern) {
801
			$this->out(__d('cake_console', ' * Updating %s', $pattern[0]), 1, Shell::VERBOSE);
802
			$contents = preg_replace($pattern[1], $pattern[2], $contents);
803
		}
804
 
805
		$this->out(__d('cake_console', 'Done updating %s', $file), 1);
806
		if (!$this->params['dry-run']) {
807
			file_put_contents($file, $contents);
808
		}
809
	}
810
 
811
/**
812
 * Gets the option parser instance and configures it.
813
 *
814
 * @return ConsoleOptionParser
815
 */
816
	public function getOptionParser() {
817
		$parser = parent::getOptionParser();
818
 
819
		$subcommandParser = array(
820
			'options' => array(
821
				'plugin' => array(
822
					'short' => 'p',
823
					'help' => __d('cake_console', 'The plugin to update. Only the specified plugin will be updated.')
824
				),
825
				'ext' => array(
826
					'short' => 'e',
827
					'help' => __d('cake_console', 'The extension(s) to search. A pipe delimited list, or a preg_match compatible subpattern'),
828
					'default' => 'php|ctp|thtml|inc|tpl'
829
				),
830
				'git' => array(
831
					'short' => 'g',
832
					'help' => __d('cake_console', 'Use git command for moving files around.'),
833
					'boolean' => true
834
				),
835
				'dry-run' => array(
836
					'short' => 'd',
837
					'help' => __d('cake_console', 'Dry run the update, no files will actually be modified.'),
838
					'boolean' => true
839
				)
840
			)
841
		);
842
 
843
		$parser->description(
844
			__d('cake_console', "A tool to help automate upgrading an application or plugin " .
845
			"from CakePHP 1.3 to 2.0. Be sure to have a backup of your application before " .
846
			"running these commands."
847
		))->addSubcommand('all', array(
848
			'help' => __d('cake_console', 'Run all upgrade commands.'),
849
			'parser' => $subcommandParser
850
		))->addSubcommand('tests', array(
851
			'help' => __d('cake_console', 'Update tests class names to FooTest rather than FooTestCase.'),
852
			'parser' => $subcommandParser
853
		))->addSubcommand('locations', array(
854
			'help' => __d('cake_console', 'Move files and folders to their new homes.'),
855
			'parser' => $subcommandParser
856
		))->addSubcommand('i18n', array(
857
			'help' => __d('cake_console', 'Update the i18n translation method calls.'),
858
			'parser' => $subcommandParser
859
		))->addSubcommand('helpers', array(
860
			'help' => __d('cake_console', 'Update calls to helpers.'),
861
			'parser' => $subcommandParser
862
		))->addSubcommand('basics', array(
863
			'help' => __d('cake_console', 'Update removed basics functions to PHP native functions.'),
864
			'parser' => $subcommandParser
865
		))->addSubcommand('request', array(
866
			'help' => __d('cake_console', 'Update removed request access, and replace with $this->request.'),
867
			'parser' => $subcommandParser
868
		))->addSubcommand('configure', array(
869
			'help' => __d('cake_console', "Update Configure::read() to Configure::read('debug')"),
870
			'parser' => $subcommandParser
871
		))->addSubcommand('constants', array(
872
			'help' => __d('cake_console', "Replace Obsolete constants"),
873
			'parser' => $subcommandParser
874
		))->addSubcommand('controller_redirects', array(
875
			'help' => __d('cake_console', 'Return early on controller redirect calls.'),
876
			'parser' => $subcommandParser
877
		))->addSubcommand('components', array(
878
			'help' => __d('cake_console', 'Update components to extend Component class.'),
879
			'parser' => $subcommandParser
880
		))->addSubcommand('exceptions', array(
881
			'help' => __d('cake_console', 'Replace use of cakeError with exceptions.'),
882
			'parser' => $subcommandParser
883
		));
884
 
885
		return $parser;
886
	}
887
 
888
}