Passed
Push — master ( ddc6cf...dbe801 )
by Fabio
08:36
created

PradoCommandLineActiveRecordGen::performAction()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 7
eloc 11
c 1
b 1
f 0
nc 8
nop 1
dl 0
loc 15
rs 8.8333
1
<?php
2
3
/**
4
 * Prado command line developer tools.
5
 *
6
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
7
 * @link https://github.com/pradosoft/prado
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 */
10
11
if (!isset($_SERVER['argv']) || php_sapi_name() !== 'cli') {
12
	die('Must be run from the command line');
13
}
14
15
// Locate composer's autoloader
16
if (file_exists($autoloader = realpath(__DIR__ . '/../vendor/autoload.php'))) {
17
	// if we are running inside a prado repo checkout, get out of bin/
18
	include($autoloader);
19
} elseif (file_exists($autoloader = realpath(__DIR__ . '/../../../autoload.php'))) {
20
	// if we are running from inside an application's vendor/ directory, get out of pradosoft/prado/bin/
21
	include($autoloader);
22
}
23
24
use Prado\TApplication;
25
use Prado\Prado;
26
use Prado\Data\ActiveRecord\TActiveRecordConfig;
27
use Prado\Data\ActiveRecord\TActiveRecordManager;
28
29
//stub application class
30
class PradoShellApplication extends TApplication
31
{
32
	public function run()
33
	{
34
		$this->initApplication();
35
	}
36
}
37
38
restore_exception_handler();
39
40
41
//register action classes
42
PradoCommandLineInterpreter::getInstance()->addActionClass('PradoCommandLineCreateProject');
43
PradoCommandLineInterpreter::getInstance()->addActionClass('PradoCommandLineCreateTests');
44
PradoCommandLineInterpreter::getInstance()->addActionClass('PradoCommandLinePhpShell');
45
PradoCommandLineInterpreter::getInstance()->addActionClass('PradoCommandLineActiveRecordGen');
46
PradoCommandLineInterpreter::getInstance()->addActionClass('PradoCommandLineActiveRecordGenAll');
47
48
//run it;
49
PradoCommandLineInterpreter::getInstance()->run($_SERVER['argv']);
50
51
/**************** END CONFIGURATION **********************/
52
53
/**
54
 * PradoCommandLineInterpreter Class
55
 *
56
 * Command line interface, configures the action classes and dispatches the command actions.
57
 *
58
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
59
 * @since 3.0.5
60
 */
61
class PradoCommandLineInterpreter
62
{
63
	/**
64
	 * @var array command action classes
65
	 */
66
	protected $_actions = [];
67
68
	/**
69
	 * @param string $class action class name
70
	 */
71
	public function addActionClass($class)
72
	{
73
		$this->_actions[$class] = new $class;
74
	}
75
76
	/**
77
	 * @return PradoCommandLineInterpreter static instance
78
	 */
79
	public static function getInstance()
80
	{
81
		static $instance;
82
		if ($instance === null) {
83
			$instance = new self;
84
		}
85
		return $instance;
86
	}
87
88
	public static function printGreeting()
89
	{
90
		echo "Command line tools for Prado " . Prado::getVersion() . ".\n";
91
	}
92
93
	/**
94
	 * Dispatch the command line actions.
95
	 * @param array $args command line arguments
96
	 */
97
	public function run($args)
98
	{
99
		if (count($args) > 1) {
100
			array_shift($args);
101
		}
102
		$valid = false;
103
		foreach ($this->_actions as $class => $action) {
104
			if ($action->isValidAction($args)) {
105
				$valid |= $action->performAction($args);
106
				break;
107
			} else {
108
				$valid = false;
109
			}
110
		}
111
		if (!$valid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $valid of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
112
			$this->printHelp();
113
		}
114
	}
115
116
	/**
117
	 * Print command line help, default action.
118
	 */
119
	public function printHelp()
120
	{
121
		PradoCommandLineInterpreter::printGreeting();
122
123
		echo "usage: php prado-cli.php action <parameter> [optional]\n";
124
		echo "example: php prado-cli.php -c mysite\n\n";
125
		echo "actions:\n";
126
		foreach ($this->_actions as $action) {
127
			echo $action->renderHelp();
128
		}
129
	}
130
}
131
132
/**
133
 * Base class for command line actions.
134
 *
135
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
136
 * @since 3.0.5
137
 */
138
abstract class PradoCommandLineAction
139
{
140
	/**
141
	 * Execute the action.
142
	 * @param array $args command line parameters
143
	 * @return bool true if action was handled
144
	 */
145
	abstract public function performAction($args);
146
147
	/**
148
	 * Creates a directory and sets its mode
149
	 * @param string $dir directory name
150
	 * @param int $mask directory mode mask suitable for chmod()
151
	 */
152
	protected function createDirectory($dir, $mask)
153
	{
154
		if (!is_dir($dir)) {
155
			mkdir($dir);
156
			echo "creating $dir\n";
157
		}
158
		if (is_dir($dir)) {
159
			chmod($dir, $mask);
160
		}
161
	}
162
163
	/**
164
	 * Creates a file and fills it with content
165
	 * @param string $filename file name
166
	 * @param int $content file contents
167
	 */
168
	protected function createFile($filename, $content)
169
	{
170
		if (!is_file($filename)) {
171
			file_put_contents($filename, $content);
172
			echo "creating $filename\n";
173
		}
174
	}
175
176
	/**
177
	 * Checks if specified parameters are suitable for the specified action
178
	 * @param array $args parameters
179
	 * @return bool
180
	 */
181
	public function isValidAction($args)
182
	{
183
		return 0 == strcasecmp($args[0], $this->action) &&
184
			count($args) - 1 >= count($this->parameters);
185
	}
186
187
	/**
188
	 * @return string
189
	 */
190
	public function renderHelp()
191
	{
192
		$params = [];
193
		foreach ($this->parameters as $v) {
194
			$params[] = '<' . $v . '>';
195
		}
196
		$parameters = implode(' ', $params);
197
		$options = [];
198
		foreach ($this->optional as $v) {
199
			$options[] = '[' . $v . ']';
200
		}
201
		$optional = (strlen($parameters) ? ' ' : '') . implode(' ', $options);
202
		$description = '';
203
		foreach (explode("\n", wordwrap($this->description, 65)) as $line) {
204
			$description .= '    ' . $line . "\n";
205
		}
206
		return <<<EOD
207
  {$this->action} {$parameters}{$optional}
208
{$description}
209
210
EOD;
211
	}
212
213
	/**
214
	 * Initalize a Prado application inside the specified directory
215
	 * @param string $directory directory name
216
	 * @return false|TApplication
217
	 */
218
	protected function initializePradoApplication($directory)
219
	{
220
		$_SERVER['SCRIPT_FILENAME'] = $directory . '/index.php';
221
		$app_dir = realpath($directory . '/protected/');
222
		if ($app_dir !== false && is_dir($app_dir)) {
223
			if (Prado::getApplication() === null) {
224
				$app = new PradoShellApplication($app_dir);
225
				$app->run();
226
				$dir = substr(str_replace(realpath('./'), '', $app_dir), 1);
227
				PradoCommandLineInterpreter::printGreeting();
228
				echo '** Loaded PRADO appplication in directory "' . $dir . "\".\n";
229
			}
230
231
			return Prado::getApplication();
232
		} else {
233
			PradoCommandLineInterpreter::printGreeting();
234
			echo '+' . str_repeat('-', 77) . "+\n";
235
			echo '** Unable to load PRADO application in directory "' . $directory . "\".\n";
236
			echo '+' . str_repeat('-', 77) . "+\n";
237
		}
238
		return false;
239
	}
240
}
241
242
/**
243
 * Create a Prado project skeleton, including directories and files.
244
 *
245
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
246
 * @since 3.0.5
247
 */
248
class PradoCommandLineCreateProject extends PradoCommandLineAction
249
{
250
	protected $action = '-c';
251
	protected $parameters = ['directory'];
252
	protected $optional = [];
253
	protected $description = 'Creates a Prado project skeleton for the given <directory>.';
254
255
	/**
256
	 * @param array $args parameters
257
	 * @return bool
258
	 */
259
	public function performAction($args)
260
	{
261
		PradoCommandLineInterpreter::printGreeting();
262
		$this->createNewPradoProject($args[1]);
263
		return true;
264
	}
265
266
	/**
267
	 * Functions to create new prado project.
268
	 * @param mixed $dir
269
	 */
270
	protected function createNewPradoProject($dir)
271
	{
272
		if (strlen(trim($dir)) == 0) {
273
			return;
274
		}
275
276
		$rootPath = realpath(dirname(trim($dir)));
277
278
		if (basename($dir) !== '.') {
279
			$basePath = $rootPath . DIRECTORY_SEPARATOR . basename($dir);
280
		} else {
281
			$basePath = $rootPath;
282
		}
283
		$appName = basename($basePath);
284
		$assetPath = $basePath . DIRECTORY_SEPARATOR . 'assets';
285
		$protectedPath = $basePath . DIRECTORY_SEPARATOR . 'protected';
286
		$runtimePath = $basePath . DIRECTORY_SEPARATOR . 'protected' . DIRECTORY_SEPARATOR . 'runtime';
287
		$pagesPath = $protectedPath . DIRECTORY_SEPARATOR . 'pages';
288
289
		$indexFile = $basePath . DIRECTORY_SEPARATOR . 'index.php';
290
		$htaccessFile = $protectedPath . DIRECTORY_SEPARATOR . '.htaccess';
291
		$configFile = $protectedPath . DIRECTORY_SEPARATOR . 'application.xml';
292
		$defaultPageFile = $pagesPath . DIRECTORY_SEPARATOR . 'Home.page';
293
294
		$this->createDirectory($basePath, 0755);
295
		$this->createDirectory($assetPath, 0777);
296
		$this->createDirectory($protectedPath, 0755);
297
		$this->createDirectory($runtimePath, 0777);
298
		$this->createDirectory($pagesPath, 0755);
299
300
		$this->createFile($indexFile, $this->renderIndexFile());
0 ignored issues
show
Bug introduced by
$this->renderIndexFile() of type string is incompatible with the type integer expected by parameter $content of PradoCommandLineAction::createFile(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

300
		$this->createFile($indexFile, /** @scrutinizer ignore-type */ $this->renderIndexFile());
Loading history...
301
		$this->createFile($configFile, $this->renderConfigFile($appName));
0 ignored issues
show
Bug introduced by
$appName of type string is incompatible with the type array expected by parameter $appName of PradoCommandLineCreateProject::renderConfigFile(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

301
		$this->createFile($configFile, $this->renderConfigFile(/** @scrutinizer ignore-type */ $appName));
Loading history...
302
		$this->createFile($htaccessFile, $this->renderHtaccessFile());
303
		$this->createFile($defaultPageFile, $this->renderDefaultPage());
304
	}
305
306
	/**
307
	 * @return string
308
	 */
309
	protected function renderIndexFile()
310
	{
311
		$framework = realpath(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'framework' . DIRECTORY_SEPARATOR . 'prado.php';
312
		return '<?php
313
314
$frameworkPath=\'' . $framework . '\';
315
316
// The following directory checks may be removed if performance is required
317
$basePath=dirname(__FILE__);
318
$assetsPath=$basePath.\'/assets\';
319
$runtimePath=$basePath.\'/protected/runtime\';
320
321
if(!is_file($frameworkPath))
322
	die("Unable to find prado framework path $frameworkPath.");
323
if(!is_writable($assetsPath))
324
	die("Please make sure that the directory $assetsPath is writable by Web server process.");
325
if(!is_writable($runtimePath))
326
	die("Please make sure that the directory $runtimePath is writable by Web server process.");
327
328
329
require_once($frameworkPath);
330
331
$application=new TApplication;
332
$application->run();
333
';
334
	}
335
336
	/**
337
	 * @param array $appName application id
338
	 * @return string
339
	 */
340
	protected function renderConfigFile($appName)
341
	{
342
		return <<<EOD
343
<?xml version="1.0" encoding="utf-8"?>
344
345
<application id="$appName" mode="Debug">
346
  <!-- alias definitions and namespace usings
347
  <paths>
348
    <alias id="myalias" path="./lib" />
349
    <using namespace="Application.common.*" />
350
  </paths>
351
  -->
352
353
  <!-- configurations for modules -->
354
  <modules>
355
    <!-- Remove this comment mark to enable caching
356
    <module id="cache" class="Prado\Caching\TDbCache" />
357
    -->
358
359
    <!-- Remove this comment mark to enable PATH url format
360
    <module id="request" class="THttpRequest" UrlFormat="Path" />
361
    -->
362
363
    <!-- Remove this comment mark to enable logging
364
    <module id="log" class="Prado\Util\TLogRouter">
365
      <route class="TBrowserLogRoute" Categories="System" />
366
    </module>
367
    -->
368
  </modules>
369
370
  <!-- configuration for available services -->
371
  <services>
372
    <service id="page" class="TPageService" DefaultPage="Home" />
373
  </services>
374
375
  <!-- application parameters
376
  <parameters>
377
    <parameter id="param1" value="value1" />
378
    <parameter id="param2" value="value2" />
379
  </parameters>
380
  -->
381
</application>
382
EOD;
383
	}
384
385
	/**
386
	 * @return string
387
	 */
388
	protected function renderHtaccessFile()
389
	{
390
		return 'deny from all';
391
	}
392
393
	/**
394
	 * @return string
395
	 */
396
	protected function renderDefaultPage()
397
	{
398
		return <<<EOD
399
<html>
400
<head>
401
  <title>Welcome to PRADO</title>
402
</head>
403
<body>
404
<h1>Welcome to PRADO!</h1>
405
</body>
406
</html>
407
EOD;
408
	}
409
}
410
411
/**
412
 * Creates test fixtures for a Prado application.
413
 *
414
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
415
 * @since 3.0.5
416
 */
417
class PradoCommandLineCreateTests extends PradoCommandLineAction
418
{
419
	protected $action = '-t';
420
	protected $parameters = ['directory'];
421
	protected $optional = [];
422
	protected $description = 'Create test fixtures in the given <directory>.';
423
424
	/**
425
	 * @param array $args parameters
426
	 * @return bool
427
	 */
428
	public function performAction($args)
429
	{
430
		PradoCommandLineInterpreter::printGreeting();
431
		$this->createTestFixtures($args[1]);
432
		return true;
433
	}
434
435
	/**
436
	 * @param string $dir directory name
437
	 */
438
	protected function createTestFixtures($dir)
439
	{
440
		if (strlen(trim($dir)) == 0) {
441
			return;
442
		}
443
444
		$rootPath = realpath(dirname(trim($dir)));
445
		$basePath = $rootPath . '/' . basename($dir);
446
447
		$tests = $basePath . '/tests';
448
		$unit_tests = $tests . '/unit';
449
		$functional_tests = $tests . '/functional';
450
451
		$this->createDirectory($tests, 0755);
452
		$this->createDirectory($unit_tests, 0755);
453
		$this->createDirectory($functional_tests, 0755);
454
455
		$unit_test_index = $tests . '/unit.php';
456
		$functional_test_index = $tests . '/functional.php';
457
458
		$this->createFile($unit_test_index, $this->renderUnitTestFixture());
0 ignored issues
show
Bug introduced by
$this->renderUnitTestFixture() of type string is incompatible with the type integer expected by parameter $content of PradoCommandLineAction::createFile(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

458
		$this->createFile($unit_test_index, /** @scrutinizer ignore-type */ $this->renderUnitTestFixture());
Loading history...
459
		$this->createFile($functional_test_index, $this->renderFunctionalTestFixture());
460
	}
461
462
	/**
463
	 * @return string
464
	 */
465
	protected function renderUnitTestFixture()
466
	{
467
		$tester = realpath(dirname(__DIR__)) . '/tests/test_tools/unit_tests.php';
468
		return '<?php
469
470
include_once \'' . $tester . '\';
471
472
$app_directory = "../protected";
473
$test_cases = dirname(__FILE__)."/unit";
474
475
$tester = new PradoUnitTester($test_cases, $app_directory);
476
$tester->run(new HtmlReporter());
477
';
478
	}
479
480
	/**
481
	 * @return string
482
	 */
483
	protected function renderFunctionalTestFixture()
484
	{
485
		$tester = realpath(dirname(__DIR__)) . '/tests/test_tools/functional_tests.php';
486
		return '<?php
487
488
include_once \'' . $tester . '\';
489
490
$test_cases = dirname(__FILE__)."/functional";
491
492
$tester=new PradoFunctionalTester($test_cases);
493
$tester->run(new SimpleReporter());
494
';
495
	}
496
}
497
498
/**
499
 * Creates and run a Prado application in a PHP Shell.
500
 *
501
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
502
 * @since 3.0.5
503
 */
504
class PradoCommandLinePhpShell extends PradoCommandLineAction
505
{
506
	protected $action = 'shell';
507
	protected $parameters = [];
508
	protected $optional = ['directory'];
509
	protected $description = 'Runs a PHP interactive interpreter. Initializes the Prado application in the given [directory].';
510
511
	/**
512
	 * @param array $args parameters
513
	 * @return bool
514
	 */
515
	public function performAction($args)
516
	{
517
		if (count($args) > 1) {
518
			$this->initializePradoApplication($args[1]);
519
		}
520
521
		\Psy\debug([], Prado::getApplication());
522
		return true;
523
	}
524
}
525
526
/**
527
 * Create active record skeleton
528
 *
529
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
530
 * @since 3.1
531
 */
532
class PradoCommandLineActiveRecordGen extends PradoCommandLineAction
533
{
534
	protected $action = 'generate';
535
	protected $parameters = ['table', 'output'];
536
	protected $optional = ['directory', 'soap'];
537
	protected $description = 'Generate Active Record skeleton for <table> to <output> file using application.xml in [directory]. May also generate [soap] properties.';
538
	private $_soap = false;
539
540
	/**
541
	 * @param array $args parameters
542
	 * @return bool
543
	 */
544
	public function performAction($args)
545
	{
546
		$app_dir = count($args) > 3 ? $this->getAppDir($args[3]) : $this->getAppDir();
547
		$this->_soap = count($args) > 4;
548
		$this->_overwrite = count($args) > 5;
0 ignored issues
show
Bug Best Practice introduced by
The property _overwrite does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
549
		if ($app_dir !== false) {
550
			$config = $this->getActiveRecordConfig($app_dir);
551
			$output = $this->getOutputFile($app_dir, $args[2]);
552
			if (is_file($output) && !$this->_overwrite) {
0 ignored issues
show
Bug introduced by
It seems like $output can also be of type false; however, parameter $filename of is_file() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

552
			if (is_file(/** @scrutinizer ignore-type */ $output) && !$this->_overwrite) {
Loading history...
553
				echo "** File $output already exists, skipping. \n";
554
			} elseif ($config !== false && $output !== false) {
0 ignored issues
show
introduced by
The condition $config !== false is always false.
Loading history...
555
				$this->generateActiveRecord($config, $args[1], $output);
556
			}
557
		}
558
		return true;
559
	}
560
561
	/**
562
	 * @param string $dir application directory
563
	 * @return false|string
564
	 */
565
	protected function getAppDir($dir = ".")
566
	{
567
		if (is_dir($dir)) {
568
			return realpath($dir);
569
		}
570
		if (false !== ($app_dir = realpath($dir . '/protected/')) && is_dir($app_dir)) {
571
			return $app_dir;
572
		}
573
		echo '** Unable to find directory "' . $dir . "\".\n";
574
		return false;
575
	}
576
577
	/**
578
	 * @param string $app_dir application directory
579
	 * @return false|string
580
	 */
581
	protected function getXmlFile($app_dir)
582
	{
583
		if (false !== ($xml = realpath($app_dir . '/application.xml')) && is_file($xml)) {
584
			return $xml;
585
		}
586
		if (false !== ($xml = realpath($app_dir . '/protected/application.xml')) && is_file($xml)) {
587
			return $xml;
588
		}
589
		echo '** Unable to find application.xml in ' . $app_dir . "\n";
590
		return false;
591
	}
592
593
	/**
594
	 * @param string $app_dir application directory
595
	 * @return false|string
596
	 */
597
	protected function getActiveRecordConfig($app_dir)
598
	{
599
		if (false === ($xml = $this->getXmlFile($app_dir))) {
600
			return false;
601
		}
602
		if (false !== ($app = $this->initializePradoApplication($app_dir))) {
603
			foreach ($app->getModules() as $module) {
604
				if ($module instanceof TActiveRecordConfig) {
605
					return $module;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $module returns the type Prado\Data\ActiveRecord\TActiveRecordConfig which is incompatible with the documented return type false|string.
Loading history...
606
				}
607
			}
608
			echo '** Unable to find TActiveRecordConfig module in ' . $xml . "\n";
609
		}
610
		return false;
611
	}
612
613
	/**
614
	 * @param string $app_dir application directory
615
	 * @param string $namespace output file in namespace format
616
	 * @return false|string
617
	 */
618
	protected function getOutputFile($app_dir, $namespace)
619
	{
620
		if (is_file($namespace) && strpos($namespace, $app_dir) === 0) {
621
			return $namespace;
622
		}
623
		$file = Prado::getPathOfNamespace($namespace, ".php");
624
		if ($file !== null && false !== ($path = realpath(dirname($file))) && is_dir($path)) {
625
			if (strpos($path, $app_dir) === 0) {
626
				return $file;
627
			}
628
		}
629
		echo '** Output file ' . $file . ' must be within directory ' . $app_dir . "\n";
630
		return false;
631
	}
632
633
	/**
634
	 * @param string $config database configuration
635
	 * @param string $tablename table name
636
	 * @param string $output output file name
637
	 * @return bool
638
	 */
639
	protected function generateActiveRecord($config, $tablename, $output)
640
	{
641
		$manager = TActiveRecordManager::getInstance();
642
		if ($manager->getDbConnection()) {
643
			$gateway = $manager->getRecordGateway();
644
			$tableInfo = $gateway->getTableInfo($manager->getDbConnection(), $tablename);
645
			if (count($tableInfo->getColumns()) === 0) {
646
				echo '** Unable to find table or view "' . $tablename . '" in "' . $manager->getDbConnection()->getConnectionString() . "\".\n";
647
				return false;
648
			} else {
649
				$properties = [];
650
				foreach ($tableInfo->getColumns() as $field => $column) {
651
					$properties[] = $this->generateProperty($field, $column);
652
				}
653
			}
654
655
			$classname = basename($output, '.php');
656
			$class = $this->generateClass($properties, $tablename, $classname);
657
			echo "  Writing class $classname to file $output\n";
658
			file_put_contents($output, $class);
659
		} else {
660
			echo '** Unable to connect to database with ConnectionID=\'' . $config->getConnectionID() . "'. Please check your settings in application.xml and ensure your database connection is set up first.\n";
661
		}
662
	}
663
664
	/**
665
	 * @param string $field php variable name
666
	 * @param string $column database column name
667
	 * @return string
668
	 */
669
	protected function generateProperty($field, $column)
670
	{
671
		$prop = '';
672
		$name = '$' . $field;
673
		$type = $column->getPHPType();
674
		if ($this->_soap) {
675
			$prop .= <<<EOD
676
677
	/**
678
	 * @var $type $name
679
	 * @soapproperty
680
	 */
681
682
EOD;
683
		}
684
		$prop .= "\tpublic $name;";
685
		return $prop;
686
	}
687
688
	/**
689
	 * @param array $properties class varibles
690
	 * @param string $tablename database table name
691
	 * @param string $class php class name
692
	 * @return string
693
	 */
694
	protected function generateClass($properties, $tablename, $class)
695
	{
696
		$props = implode("\n", $properties);
697
		$date = date('Y-m-d h:i:s');
698
		return <<<EOD
699
<?php
700
/**
701
 * Auto generated by prado-cli.php on $date.
702
 */
703
class $class extends TActiveRecord
704
{
705
	const TABLE='$tablename';
706
707
$props
708
709
	public static function finder(\$className=__CLASS__)
710
	{
711
		return parent::finder(\$className);
712
	}
713
}
714
715
EOD;
716
	}
717
}
718
719
/**
720
 * Create active record skeleton for all tables in DB and its relations
721
 *
722
 * @author Matthias Endres <me[at]me23[dot]de>
723
 * @author Daniel Sampedro Bello <darthdaniel85[at]gmail[dot]com>
724
 * @since 3.2
725
 */
726
class PradoCommandLineActiveRecordGenAll extends PradoCommandLineAction
727
{
728
	protected $action = 'generateAll';
729
	protected $parameters = ['output'];
730
	protected $optional = ['directory', 'soap', 'overwrite', 'prefix', 'postfix'];
731
	protected $description = "Generate Active Record skeleton for all Tables to <output> file using application.xml in [directory]. May also generate [soap] properties.\nGenerated Classes are named like the Table with optional [Prefix] and/or [Postfix]. [Overwrite] is used to overwrite existing Files.";
732
	private $_soap = false;
733
	private $_prefix = '';
734
	private $_postfix = '';
735
	private $_overwrite = false;
736
737
	/**
738
	 * @param array $args parameters
739
	 * @return bool
740
	 */
741
	public function performAction($args)
742
	{
743
		$app_dir = count($args) > 2 ? $this->getAppDir($args[2]) : $this->getAppDir();
744
		$this->_soap = count($args) > 3 ? ($args[3] == "soap" || $args[3] == "true" ? true : false) : false;
745
		$this->_overwrite = count($args) > 4 ? ($args[4] == "overwrite" || $args[4] == "true" ? true : false) : false;
746
		$this->_prefix = count($args) > 5 ? $args[5] : '';
747
		$this->_postfix = count($args) > 6 ? $args[6] : '';
748
749
		if ($app_dir !== false) {
750
			$config = $this->getActiveRecordConfig($app_dir);
751
752
			$manager = TActiveRecordManager::getInstance();
753
			$con = $manager->getDbConnection();
754
			$con->Active = true;
755
756
			switch ($con->getDriverName()) {
757
				case 'mysqli':
758
				case 'mysql':
759
					$command = $con->createCommand("SHOW TABLES");
760
					break;
761
				case 'sqlite': //sqlite 3
762
				case 'sqlite2': //sqlite 2
763
					$command = $con->createCommand("SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name<>'sqlite_sequence'");
764
					break;
765
				case 'pgsql':
766
				case 'mssql': // Mssql driver on windows hosts
767
				case 'sqlsrv': // sqlsrv driver on windows hosts
768
				case 'dblib': // dblib drivers on linux (and maybe others os) hosts
769
				case 'oci':
770
//				case 'ibm':
771
				default:
772
					echo "\n    Sorry, generateAll is not implemented for " . $con->getDriverName() . "\n";
773
774
			   }
775
776
			$dataReader = $command->query();
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $command does not seem to be defined for all execution paths leading up to this point.
Loading history...
777
			$dataReader->bindColumn(1, $table);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $table does not exist. Did you maybe mean $tables?
Loading history...
778
			$tables = [];
779
			while ($dataReader->read() !== false) {
780
				$tables[] = $table;
781
			}
782
			$con->Active = false;
783
			foreach ($tables as $key => $table) {
784
				$output = $args[1] . "." . $this->_prefix . ucfirst($table) . $this->_postfix;
785
				if ($config !== false && $output !== false) {
786
					$this->generate("generate " . $table . " " . $output . " " . $this->_soap . " " . $this->_overwrite);
787
				}
788
			}
789
		}
790
		return true;
791
	}
792
793
	/**
794
	 * @param string $l commandline
795
	 */
796
	public function generate($l)
797
	{
798
		$input = explode(" ", trim($l));
799
		if (count($input) > 2) {
800
			$app_dir = '.';
801
			if (Prado::getApplication() !== null) {
802
				$app_dir = dirname(Prado::getApplication()->getBasePath());
803
			}
804
			$args = [$input[0], $input[1], $input[2], $app_dir];
805
			if (count($input) > 3) {
806
				$args[] = 'soap';
807
			}
808
			if (count($input) > 4) {
809
				$args[] = 'overwrite';
810
			}
811
			$cmd = new PradoCommandLineActiveRecordGen;
812
			$cmd->performAction($args);
813
		} else {
814
			echo "\n    Usage: generate table_name Application.pages.RecordClassName\n";
815
		}
816
	}
817
818
	/**
819
	 * @param string $dir application directory
820
	 * @return false|string
821
	 */
822
	protected function getAppDir($dir = ".")
823
	{
824
		if (is_dir($dir)) {
825
			return realpath($dir);
826
		}
827
		if (false !== ($app_dir = realpath($dir . '/protected/')) && is_dir($app_dir)) {
828
			return $app_dir;
829
		}
830
		echo '** Unable to find directory "' . $dir . "\".\n";
831
		return false;
832
	}
833
834
	/**
835
	 * @param string $app_dir application directory
836
	 * @return false|string
837
	 */
838
	protected function getXmlFile($app_dir)
839
	{
840
		if (false !== ($xml = realpath($app_dir . '/application.xml')) && is_file($xml)) {
841
			return $xml;
842
		}
843
		if (false !== ($xml = realpath($app_dir . '/protected/application.xml')) && is_file($xml)) {
844
			return $xml;
845
		}
846
		echo '** Unable to find application.xml in ' . $app_dir . "\n";
847
		return false;
848
	}
849
850
	/**
851
	 * @param string $app_dir application directory
852
	 * @return false|string
853
	 */
854
	protected function getActiveRecordConfig($app_dir)
855
	{
856
		if (false === ($xml = $this->getXmlFile($app_dir))) {
857
			return false;
858
		}
859
		if (false !== ($app = $this->initializePradoApplication($app_dir))) {
860
			foreach ($app->getModules() as $module) {
861
				if ($module instanceof TActiveRecordConfig) {
862
					return $module;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $module returns the type Prado\Data\ActiveRecord\TActiveRecordConfig which is incompatible with the documented return type false|string.
Loading history...
863
				}
864
			}
865
			echo '** Unable to find TActiveRecordConfig module in ' . $xml . "\n";
866
		}
867
		return false;
868
	}
869
870
	/**
871
	 * @param string $app_dir application directory
872
	 * @param string $namespace output file in namespace format
873
	 * @return false|string
874
	 */
875
	protected function getOutputFile($app_dir, $namespace)
876
	{
877
		if (is_file($namespace) && strpos($namespace, $app_dir) === 0) {
878
			return $namespace;
879
		}
880
		$file = Prado::getPathOfNamespace($namespace, "");
881
		if ($file !== null && false !== ($path = realpath(dirname($file))) && is_dir($path)) {
882
			if (strpos($path, $app_dir) === 0) {
883
				return $file;
884
			}
885
		}
886
		echo '** Output file ' . $file . ' must be within directory ' . $app_dir . "\n";
887
		return false;
888
	}
889
}
890