Completed
Branch master (099915)
by Fabio
08:02
created

PradoCommandLineCreateProject::renderConfigFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 6
rs 10
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
27
//stub application class
28
class PradoShellApplication extends TApplication
29
{
30
	public function run()
31
	{
32
		$this->initApplication();
33
	}
34
}
35
36
restore_exception_handler();
37
38
39
//register action classes
40
PradoCommandLineInterpreter::getInstance()->addActionClass('PradoCommandLineCreateProject');
41
PradoCommandLineInterpreter::getInstance()->addActionClass('PradoCommandLineCreateTests');
42
PradoCommandLineInterpreter::getInstance()->addActionClass('PradoCommandLinePhpShell');
43
PradoCommandLineInterpreter::getInstance()->addActionClass('PradoCommandLineActiveRecordGen');
44
PradoCommandLineInterpreter::getInstance()->addActionClass('PradoCommandLineActiveRecordGenAll');
45
46
//run it;
47
PradoCommandLineInterpreter::getInstance()->run($_SERVER['argv']);
48
49
/**************** END CONFIGURATION **********************/
50
51
/**
52
 * PradoCommandLineInterpreter Class
53
 *
54
 * Command line interface, configures the action classes and dispatches the command actions.
55
 *
56
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
57
 * @since 3.0.5
58
 */
59
class PradoCommandLineInterpreter
60
{
61
	/**
62
	 * @var array command action classes
63
	 */
64
	protected $_actions = [];
65
66
	/**
67
	 * @param string $class action class name
68
	 */
69
	public function addActionClass($class)
70
	{
71
		$this->_actions[$class] = new $class;
72
	}
73
74
	/**
75
	 * @return PradoCommandLineInterpreter static instance
76
	 */
77
	public static function getInstance()
78
	{
79
		static $instance;
80
		if ($instance === null) {
81
			$instance = new self;
82
		}
83
		return $instance;
84
	}
85
86
	public static function printGreeting()
87
	{
88
		echo "Command line tools for Prado " . Prado::getVersion() . ".\n";
89
	}
90
91
	/**
92
	 * Dispatch the command line actions.
93
	 * @param array $args command line arguments
94
	 */
95
	public function run($args)
96
	{
97
		if (count($args) > 1) {
98
			array_shift($args);
99
		}
100
		$valid = false;
101
		foreach ($this->_actions as $class => $action) {
102
			if ($action->isValidAction($args)) {
103
				$valid |= $action->performAction($args);
104
				break;
105
			} else {
106
				$valid = false;
107
			}
108
		}
109
		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...
110
			$this->printHelp();
111
		}
112
	}
113
114
	/**
115
	 * Print command line help, default action.
116
	 */
117
	public function printHelp()
118
	{
119
		PradoCommandLineInterpreter::printGreeting();
120
121
		echo "usage: php prado-cli.php action <parameter> [optional]\n";
122
		echo "example: php prado-cli.php -c mysite\n\n";
123
		echo "actions:\n";
124
		foreach ($this->_actions as $action) {
125
			echo $action->renderHelp();
126
		}
127
	}
128
}
129
130
/**
131
 * Base class for command line actions.
132
 *
133
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
134
 * @since 3.0.5
135
 */
136
abstract class PradoCommandLineAction
137
{
138
	/**
139
	 * Execute the action.
140
	 * @param array $args command line parameters
141
	 * @return bool true if action was handled
142
	 */
143
	abstract public function performAction($args);
144
145
	/**
146
	 * Creates a directory and sets its mode
147
	 * @param string $dir directory name
148
	 * @param int $mask directory mode mask suitable for chmod()
149
	 */
150
	protected function createDirectory($dir, $mask)
151
	{
152
		if (!is_dir($dir)) {
153
			mkdir($dir);
154
			echo "creating $dir\n";
155
		}
156
		if (is_dir($dir)) {
157
			chmod($dir, $mask);
158
		}
159
	}
160
161
	/**
162
	 * Creates a file and fills it with content
163
	 * @param string $filename file name
164
	 * @param int $content file contents
165
	 */
166
	protected function createFile($filename, $content)
167
	{
168
		if (!is_file($filename)) {
169
			file_put_contents($filename, $content);
170
			echo "creating $filename\n";
171
		}
172
	}
173
174
	/**
175
	 * Checks if specified parameters are suitable for the specified action
176
	 * @param array $args parameters
177
	 * @return bool
178
	 */
179
	public function isValidAction($args)
180
	{
181
		return 0 == strcasecmp($args[0], $this->action) &&
182
			count($args) - 1 >= count($this->parameters);
183
	}
184
185
	/**
186
	 * @return string
187
	 */
188
	public function renderHelp()
189
	{
190
		$params = [];
191
		foreach ($this->parameters as $v) {
192
			$params[] = '<' . $v . '>';
193
		}
194
		$parameters = implode($params, ' ');
0 ignored issues
show
Unused Code introduced by
The call to implode() has too many arguments starting with ' '. ( Ignorable by Annotation )

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

194
		$parameters = /** @scrutinizer ignore-call */ implode($params, ' ');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
195
		$options = [];
196
		foreach ($this->optional as $v) {
197
			$options[] = '[' . $v . ']';
198
		}
199
		$optional = (strlen($parameters) ? ' ' : '') . implode($options, ' ');
200
		$description = '';
201
		foreach (explode("\n", wordwrap($this->description, 65)) as $line) {
202
			$description .= '    ' . $line . "\n";
203
		}
204
		return <<<EOD
205
  {$this->action} {$parameters}{$optional}
206
{$description}
207
208
EOD;
209
	}
210
211
	/**
212
	 * Initalize a Prado application inside the specified directory
213
	 * @param string $directory directory name
214
	 * @return false|TApplication
215
	 */
216
	protected function initializePradoApplication($directory)
217
	{
218
		$_SERVER['SCRIPT_FILENAME'] = $directory . '/index.php';
219
		$app_dir = realpath($directory . '/protected/');
220
		if ($app_dir !== false && is_dir($app_dir)) {
221
			if (Prado::getApplication() === null) {
222
				$app = new PradoShellApplication($app_dir);
223
				$app->run();
224
				$dir = substr(str_replace(realpath('./'), '', $app_dir), 1);
225
				PradoCommandLineInterpreter::printGreeting();
226
				echo '** Loaded PRADO appplication in directory "' . $dir . "\".\n";
227
			}
228
229
			return Prado::getApplication();
230
		} else {
231
			PradoCommandLineInterpreter::printGreeting();
232
			echo '+' . str_repeat('-', 77) . "+\n";
233
			echo '** Unable to load PRADO application in directory "' . $directory . "\".\n";
234
			echo '+' . str_repeat('-', 77) . "+\n";
235
		}
236
		return false;
237
	}
238
}
239
240
/**
241
 * Create a Prado project skeleton, including directories and files.
242
 *
243
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
244
 * @since 3.0.5
245
 */
246
class PradoCommandLineCreateProject extends PradoCommandLineAction
247
{
248
	protected $action = '-c';
249
	protected $parameters = ['directory'];
250
	protected $optional = [];
251
	protected $description = 'Creates a Prado project skeleton for the given <directory>.';
252
253
	/**
254
	 * @param array $args parameters
255
	 * @return bool
256
	 */
257
	public function performAction($args)
258
	{
259
		PradoCommandLineInterpreter::printGreeting();
260
		$this->createNewPradoProject($args[1]);
261
		return true;
262
	}
263
264
	/**
265
	 * Functions to create new prado project.
266
	 * @param mixed $dir
267
	 */
268
	protected function createNewPradoProject($dir)
269
	{
270
		if (strlen(trim($dir)) == 0) {
271
			return;
272
		}
273
274
		$rootPath = realpath(dirname(trim($dir)));
275
276
		if (basename($dir) !== '.') {
277
			$basePath = $rootPath . DIRECTORY_SEPARATOR . basename($dir);
278
		} else {
279
			$basePath = $rootPath;
280
		}
281
		$appName = basename($basePath);
282
		$assetPath = $basePath . DIRECTORY_SEPARATOR . 'assets';
283
		$protectedPath = $basePath . DIRECTORY_SEPARATOR . 'protected';
284
		$runtimePath = $basePath . DIRECTORY_SEPARATOR . 'protected' . DIRECTORY_SEPARATOR . 'runtime';
285
		$pagesPath = $protectedPath . DIRECTORY_SEPARATOR . 'pages';
286
287
		$indexFile = $basePath . DIRECTORY_SEPARATOR . 'index.php';
288
		$htaccessFile = $protectedPath . DIRECTORY_SEPARATOR . '.htaccess';
289
		$configFile = $protectedPath . DIRECTORY_SEPARATOR . 'application.xml';
290
		$defaultPageFile = $pagesPath . DIRECTORY_SEPARATOR . 'Home.page';
291
292
		$this->createDirectory($basePath, 0755);
293
		$this->createDirectory($assetPath, 0777);
294
		$this->createDirectory($protectedPath, 0755);
295
		$this->createDirectory($runtimePath, 0777);
296
		$this->createDirectory($pagesPath, 0755);
297
298
		$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

298
		$this->createFile($indexFile, /** @scrutinizer ignore-type */ $this->renderIndexFile());
Loading history...
299
		$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

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

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

549
			if (is_file(/** @scrutinizer ignore-type */ $output)) {
Loading history...
550
				echo "** File $output already exists, skiping. \n";
551
			} elseif ($config !== false && $output !== false) {
0 ignored issues
show
introduced by
The condition $config !== false is always false.
Loading history...
552
				$this->generateActiveRecord($config, $args[1], $output);
553
			}
554
		}
555
		return true;
556
	}
557
558
	/**
559
	 * @param string $dir application directory
560
	 * @return false|string
561
	 */
562
	protected function getAppDir($dir = ".")
563
	{
564
		if (is_dir($dir)) {
565
			return realpath($dir);
566
		}
567
		if (false !== ($app_dir = realpath($dir . '/protected/')) && is_dir($app_dir)) {
568
			return $app_dir;
569
		}
570
		echo '** Unable to find directory "' . $dir . "\".\n";
571
		return false;
572
	}
573
574
	/**
575
	 * @param string $app_dir application directory
576
	 * @return false|string
577
	 */
578
	protected function getXmlFile($app_dir)
579
	{
580
		if (false !== ($xml = realpath($app_dir . '/application.xml')) && is_file($xml)) {
581
			return $xml;
582
		}
583
		if (false !== ($xml = realpath($app_dir . '/protected/application.xml')) && is_file($xml)) {
584
			return $xml;
585
		}
586
		echo '** Unable to find application.xml in ' . $app_dir . "\n";
587
		return false;
588
	}
589
590
	/**
591
	 * @param string $app_dir application directory
592
	 * @return false|string
593
	 */
594
	protected function getActiveRecordConfig($app_dir)
595
	{
596
		if (false === ($xml = $this->getXmlFile($app_dir))) {
597
			return false;
598
		}
599
		if (false !== ($app = $this->initializePradoApplication($app_dir))) {
600
			Prado::using('Prado\Data\ActiveRecord\TActiveRecordConfig');
601
			foreach ($app->getModules() as $module) {
602
				if ($module instanceof TActiveRecordConfig) {
0 ignored issues
show
Bug introduced by
The type TActiveRecordConfig was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
603
					return $module;
604
				}
605
			}
606
			echo '** Unable to find TActiveRecordConfig module in ' . $xml . "\n";
607
		}
608
		return false;
609
	}
610
611
	/**
612
	 * @param string $app_dir application directory
613
	 * @param string $namespace output file in namespace format
614
	 * @return false|string
615
	 */
616
	protected function getOutputFile($app_dir, $namespace)
617
	{
618
		if (is_file($namespace) && strpos($namespace, $app_dir) === 0) {
619
			return $namespace;
620
		}
621
		$file = Prado::getPathOfNamespace($namespace, ".php");
622
		if ($file !== null && false !== ($path = realpath(dirname($file))) && is_dir($path)) {
623
			if (strpos($path, $app_dir) === 0) {
624
				return $file;
625
			}
626
		}
627
		echo '** Output file ' . $file . ' must be within directory ' . $app_dir . "\n";
628
		return false;
629
	}
630
631
	/**
632
	 * @param string $config database configuration
633
	 * @param string $tablename table name
634
	 * @param string $output output file name
635
	 * @return bool
636
	 */
637
	protected function generateActiveRecord($config, $tablename, $output)
638
	{
639
		$manager = TActiveRecordManager::getInstance();
0 ignored issues
show
Bug introduced by
The type TActiveRecordManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
640
		if ($manager->getDbConnection()) {
641
			$gateway = $manager->getRecordGateway();
642
			$tableInfo = $gateway->getTableInfo($manager->getDbConnection(), $tablename);
643
			if (count($tableInfo->getColumns()) === 0) {
644
				echo '** Unable to find table or view "' . $tablename . '" in "' . $manager->getDbConnection()->getConnectionString() . "\".\n";
645
				return false;
646
			} else {
647
				$properties = [];
648
				foreach ($tableInfo->getColumns() as $field => $column) {
649
					$properties[] = $this->generateProperty($field, $column);
650
				}
651
			}
652
653
			$classname = basename($output, '.php');
654
			$class = $this->generateClass($properties, $tablename, $classname);
655
			echo "  Writing class $classname to file $output\n";
656
			file_put_contents($output, $class);
657
		} else {
658
			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";
659
		}
660
	}
661
662
	/**
663
	 * @param string $field php variable name
664
	 * @param string $column database column name
665
	 * @return string
666
	 */
667
	protected function generateProperty($field, $column)
668
	{
669
		$prop = '';
670
		$name = '$' . $field;
671
		$type = $column->getPHPType();
672
		if ($this->_soap) {
673
			$prop .= <<<EOD
674
675
	/**
676
	 * @var $type $name
677
	 * @soapproperty
678
	 */
679
680
EOD;
681
		}
682
		$prop .= "\tpublic $name;";
683
		return $prop;
684
	}
685
686
	/**
687
	 * @param array $properties class varibles
688
	 * @param string $tablename database table name
689
	 * @param string $class php class name
690
	 * @return string
691
	 */
692
	protected function generateClass($properties, $tablename, $class)
693
	{
694
		$props = implode("\n", $properties);
695
		$date = date('Y-m-d h:i:s');
696
		return <<<EOD
697
<?php
698
/**
699
 * Auto generated by prado-cli.php on $date.
700
 */
701
class $class extends TActiveRecord
702
{
703
	const TABLE='$tablename';
704
705
$props
706
707
	public static function finder(\$className=__CLASS__)
708
	{
709
		return parent::finder(\$className);
710
	}
711
}
712
713
EOD;
714
	}
715
}
716
717
/**
718
 * Create active record skeleton for all tables in DB and its relations
719
 *
720
 * @author Matthias Endres <me[at]me23[dot]de>
721
 * @author Daniel Sampedro Bello <darthdaniel85[at]gmail[dot]com>
722
 * @since 3.2
723
 */
724
class PradoCommandLineActiveRecordGenAll extends PradoCommandLineAction
725
{
726
	protected $action = 'generateAll';
727
	protected $parameters = ['output'];
728
	protected $optional = ['directory', 'soap', 'overwrite', 'prefix', 'postfix'];
729
	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.";
730
	private $_soap = false;
731
	private $_prefix = '';
732
	private $_postfix = '';
733
	private $_overwrite = false;
734
735
	/**
736
	 * @param array $args parameters
737
	 * @return bool
738
	 */
739
	public function performAction($args)
740
	{
741
		$app_dir = count($args) > 2 ? $this->getAppDir($args[2]) : $this->getAppDir();
742
		$this->_soap = count($args) > 3 ? ($args[3] == "soap" || $args[3] == "true" ? true : false) : false;
743
		$this->_overwrite = count($args) > 4 ? ($args[4] == "overwrite" || $args[4] == "true" ? true : false) : false;
744
		$this->_prefix = count($args) > 5 ? $args[5] : '';
745
		$this->_postfix = count($args) > 6 ? $args[6] : '';
746
747
		if ($app_dir !== false) {
748
			$config = $this->getActiveRecordConfig($app_dir);
749
750
			$manager = TActiveRecordManager::getInstance();
751
			$con = $manager->getDbConnection();
752
			$con->Active = true;
753
754
			switch ($con->getDriverName()) {
755
				case 'mysqli':
756
				case 'mysql':
757
					$command = $con->createCommand("SHOW TABLES");
758
					break;
759
				case 'sqlite': //sqlite 3
760
				case 'sqlite2': //sqlite 2
761
					$command = $con->createCommand("SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name<>'sqlite_sequence'");
762
					break;
763
				case 'pgsql':
764
				case 'mssql': // Mssql driver on windows hosts
765
				case 'sqlsrv': // sqlsrv driver on windows hosts
766
				case 'dblib': // dblib drivers on linux (and maybe others os) hosts
767
				case 'oci':
768
//				case 'ibm':
769
				default:
770
					echo "\n    Sorry, generateAll is not implemented for " . $con->getDriverName() . "\n";
771
772
			   }
773
774
			$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...
775
			$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...
776
			$tables = [];
777
			while ($dataReader->read() !== false) {
778
				$tables[] = $table;
779
			}
780
			$con->Active = false;
781
			foreach ($tables as $key => $table) {
782
				$output = $args[1] . "." . $this->_prefix . ucfirst($table) . $this->_postfix;
783
				if ($config !== false && $output !== false) {
784
					$this->generate("generate " . $table . " " . $output . " " . $this->_soap . " " . $this->_overwrite);
785
				}
786
			}
787
		}
788
		return true;
789
	}
790
791
	/**
792
	 * @param string $l commandline
793
	 */
794
	public function generate($l)
795
	{
796
		$input = explode(" ", trim($l));
797
		if (count($input) > 2) {
798
			$app_dir = '.';
799
			if (Prado::getApplication() !== null) {
800
				$app_dir = dirname(Prado::getApplication()->getBasePath());
801
			}
802
			$args = [$input[0], $input[1], $input[2], $app_dir];
803
			if (count($input) > 3) {
804
				$args = [$input[0], $input[1], $input[2], $app_dir, 'soap'];
805
			}
806
			$cmd = new PradoCommandLineActiveRecordGen;
807
			$cmd->performAction($args);
808
		} else {
809
			echo "\n    Usage: generate table_name Application.pages.RecordClassName\n";
810
		}
811
	}
812
813
	/**
814
	 * @param string $dir application directory
815
	 * @return false|string
816
	 */
817
	protected function getAppDir($dir = ".")
818
	{
819
		if (is_dir($dir)) {
820
			return realpath($dir);
821
		}
822
		if (false !== ($app_dir = realpath($dir . '/protected/')) && is_dir($app_dir)) {
823
			return $app_dir;
824
		}
825
		echo '** Unable to find directory "' . $dir . "\".\n";
826
		return false;
827
	}
828
829
	/**
830
	 * @param string $app_dir application directory
831
	 * @return false|string
832
	 */
833
	protected function getXmlFile($app_dir)
834
	{
835
		if (false !== ($xml = realpath($app_dir . '/application.xml')) && is_file($xml)) {
836
			return $xml;
837
		}
838
		if (false !== ($xml = realpath($app_dir . '/protected/application.xml')) && is_file($xml)) {
839
			return $xml;
840
		}
841
		echo '** Unable to find application.xml in ' . $app_dir . "\n";
842
		return false;
843
	}
844
845
	/**
846
	 * @param string $app_dir application directory
847
	 * @return false|string
848
	 */
849
	protected function getActiveRecordConfig($app_dir)
850
	{
851
		if (false === ($xml = $this->getXmlFile($app_dir))) {
852
			return false;
853
		}
854
		if (false !== ($app = $this->initializePradoApplication($app_dir))) {
855
			Prado::using('Prado\Data\ActiveRecord\TActiveRecordConfig');
856
			foreach ($app->getModules() as $module) {
857
				if ($module instanceof TActiveRecordConfig) {
858
					return $module;
859
				}
860
			}
861
			echo '** Unable to find TActiveRecordConfig module in ' . $xml . "\n";
862
		}
863
		return false;
864
	}
865
866
	/**
867
	 * @param string $app_dir application directory
868
	 * @param string $namespace output file in namespace format
869
	 * @return false|string
870
	 */
871
	protected function getOutputFile($app_dir, $namespace)
872
	{
873
		if (is_file($namespace) && strpos($namespace, $app_dir) === 0) {
874
			return $namespace;
875
		}
876
		$file = Prado::getPathOfNamespace($namespace, "");
877
		if ($file !== null && false !== ($path = realpath(dirname($file))) && is_dir($path)) {
878
			if (strpos($path, $app_dir) === 0) {
879
				return $file;
880
			}
881
		}
882
		echo '** Output file ' . $file . ' must be within directory ' . $app_dir . "\n";
883
		return false;
884
	}
885
}
886