generate.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. <?php
  2. /**
  3. * Fuel is a fast, lightweight, community driven PHP5 framework.
  4. *
  5. * @package Fuel
  6. * @version 1.5
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2013 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Oil;
  13. /**
  14. * Oil\Generate Class
  15. *
  16. * @package Fuel
  17. * @subpackage Oil
  18. * @category Core
  19. * @author Phil Sturgeon
  20. */
  21. class Generate
  22. {
  23. public static $create_folders = array();
  24. public static $create_files = array();
  25. public static $scaffolding = false;
  26. private static $_default_constraints = array(
  27. 'varchar' => 255,
  28. 'char' => 255,
  29. 'int' => 11
  30. );
  31. public static function config($args)
  32. {
  33. $file = strtolower(array_shift($args));
  34. if (empty($file))
  35. {
  36. throw new Exception('No config filename has been provided.');
  37. }
  38. $config = array();
  39. // load the config
  40. if ($paths = \Finder::search('config', $file, '.php', true))
  41. {
  42. // Reverse the file list so that we load the core configs first and
  43. // the app can override anything.
  44. $paths = array_reverse($paths);
  45. foreach ($paths as $path)
  46. {
  47. $config = \Fuel::load($path) + $config;
  48. }
  49. }
  50. unset($path);
  51. // We always pass in fields to a config, so lets sort them out here.
  52. foreach ($args as $conf)
  53. {
  54. // Each paramater for a config is seperated by the : character
  55. $parts = explode(":", $conf);
  56. // We must have the 'name:value' if nothing else!
  57. if (count($parts) >= 2)
  58. {
  59. $config[$parts[0]] = $parts[1];
  60. }
  61. }
  62. $overwrite = (\Cli::option('o') or \Cli::option('overwrite'));
  63. // strip whitespace and add tab
  64. $export = str_replace(array(' ', 'array ('), array("\t", 'array('), var_export($config, true));
  65. $content = '<?php'.PHP_EOL.PHP_EOL.'return '.$export.';';
  66. $content .= <<<CONF
  67. /* End of file $file.php */
  68. CONF;
  69. $module = \Cli::option('module', \Cli::option('m'));
  70. // add support for `php oil g config module::file arg1:value1`
  71. if (strpos($file, '::') !== false)
  72. {
  73. list($module, $file) = explode('::', $file);
  74. }
  75. // get the namespace path (if available)
  76. if ( ! empty($module) and $path = \Autoloader::namespace_path('\\'.ucfirst($module)))
  77. {
  78. // strip the classes directory as we need the module root
  79. // and construct the filename
  80. $path = substr($path,0, -8).'config'.DS.$file.'.php';
  81. $path_name = "\\".ucfirst($module).'::';
  82. }
  83. elseif ( ! empty($module))
  84. {
  85. throw new Exception("{$module} need to be loaded first, please use config always_load.modules.");
  86. }
  87. else
  88. {
  89. $path = APPPATH.'config'.DS.$file.'.php';
  90. $path_name = 'APPPATH/';
  91. }
  92. if ( ! $overwrite and is_file($path))
  93. {
  94. throw new Exception("{$path_name}/config/{$file}.php already exist, please use --overwrite option to force update");
  95. }
  96. $path = pathinfo($path);
  97. try
  98. {
  99. \File::update($path['dirname'], $path['basename'], $content);
  100. \Cli::write("Created config: {$path_name}config/{$file}.php", 'green');
  101. }
  102. catch (\InvalidPathException $e)
  103. {
  104. throw new Exception("Invalid basepath, cannot update at ".$path_name."config".DS."{$file}.php");
  105. }
  106. catch (\FileAccessException $e)
  107. {
  108. throw new Exception($path_name."config".DS.$file.".php could not be written.");
  109. }
  110. }
  111. public static function controller($args, $build = true)
  112. {
  113. if ( ! ($name = \Str::lower(array_shift($args))))
  114. {
  115. throw new Exception('No controller name was provided.');
  116. }
  117. // Do we want a view or a viewmodel?
  118. $with_viewmodel = \Cli::option('with-viewmodel');
  119. $actions = $args;
  120. $filename = trim(str_replace(array('_', '-'), DS, $name), DS);
  121. $filepath = APPPATH.'classes'.DS.'controller'.DS.$filename.'.php';
  122. // Uppercase each part of the class name and remove hyphens
  123. $class_name = \Inflector::classify(str_replace(array('\\', '/'), '_', $name), false);
  124. // Stick "blog" to the start of the array
  125. array_unshift($args, $filename);
  126. // Create views folder and each view file
  127. if (\Cli::option('crud'))
  128. {
  129. static::views($args, 'scaffolding'.DS.'crud'.DS.'views', false);
  130. }
  131. else
  132. {
  133. static::views($args, 'scaffolding'.DS.'orm'.DS.'views', false);
  134. }
  135. $actions or $actions = array('index');
  136. $action_str = '';
  137. foreach ($actions as $action)
  138. {
  139. $action_str .= '
  140. public function action_'.$action.'()
  141. {
  142. $this->template->title = \'' . \Inflector::humanize($name) .' &raquo; ' . \Inflector::humanize($action) . '\';
  143. $this->template->content = View::forge(\''.$filename.'/' . $action .'\');
  144. }'.PHP_EOL;
  145. }
  146. $extends = \Cli::option('extends', 'Controller_Template');
  147. // Build Controller
  148. $controller = <<<CONTROLLER
  149. <?php
  150. class Controller_{$class_name} extends {$extends}
  151. {
  152. {$action_str}
  153. }
  154. CONTROLLER;
  155. // Write controller
  156. static::create($filepath, $controller, 'controller');
  157. // Do you want a viewmodel with that?
  158. if ($with_viewmodel)
  159. {
  160. $viewmodel_filepath = APPPATH.'classes'.DS.'view'.DS.$filename;
  161. // One ViewModel per action
  162. foreach ($actions as $action)
  163. {
  164. $viewmodel = <<<VIEWMODEL
  165. <?php
  166. class View_{$class_name}_{$action} extends Viewmodel
  167. {
  168. public function view()
  169. {
  170. \$this->content = "{$class_name} &raquo; {$action}";
  171. }
  172. }
  173. VIEWMODEL;
  174. // Write viewmodel
  175. static::create($viewmodel_filepath.DS.$action.'.php', $viewmodel, 'viewmodel');
  176. }
  177. }
  178. $build and static::build();
  179. }
  180. public static function model($args, $build = true)
  181. {
  182. $singular = \Inflector::singularize(\Str::lower(array_shift($args)));
  183. if (empty($singular) or strpos($singular, ':'))
  184. {
  185. throw new Exception("Command is invalid.".PHP_EOL."\tphp oil g model <modelname> [<fieldname1>:<type1> |<fieldname2>:<type2> |..]");
  186. }
  187. if (empty($args))
  188. {
  189. throw new Exception('No fields have been provided, the model will not know how to build the table.');
  190. }
  191. $plural = \Cli::option('singular') ? $singular : \Inflector::pluralize($singular);
  192. $filename = trim(str_replace(array('_', '-'), DS, $singular), DS);
  193. $filepath = APPPATH.'classes'.DS.'model'.DS.$filename.'.php';
  194. // Uppercase each part of the class name and remove hyphens
  195. $class_name = \Inflector::classify(str_replace(array('\\', '/'), '_', $singular), false);
  196. // Turn foo:string into "id", "foo",
  197. $properties = implode(",\n\t\t", array_map(function($field) {
  198. // Only take valid fields
  199. if (($field = strstr($field, ':', true)))
  200. {
  201. return "'".$field."'";
  202. }
  203. }, $args));
  204. // Make sure an id is present
  205. strpos($properties, "'id'") === false and $properties = "'id',\n\t\t".$properties.',';
  206. $contents = '';
  207. if (\Cli::option('crud'))
  208. {
  209. if ( ! \Cli::option('no-properties'))
  210. {
  211. $contents = <<<CONTENTS
  212. protected static \$_properties = array(
  213. {$properties}
  214. );
  215. CONTENTS;
  216. }
  217. if($created_at = \Cli::option('created-at'))
  218. {
  219. is_string($created_at) or $created_at = 'created_at';
  220. $contents .= <<<CONTENTS
  221. protected static \$_created_at = '$created_at';
  222. CONTENTS;
  223. }
  224. if($updated_at = \Cli::option('updated-at'))
  225. {
  226. is_string($updated_at) or $updated_at = 'updated_at';
  227. $contents .= <<<CONTENTS
  228. protected static \$_updated_at = '$updated_at';
  229. CONTENTS;
  230. }
  231. if(\Cli::option('mysql-timestamp'))
  232. {
  233. $contents .= <<<CONTENTS
  234. protected static \$_mysql_timestamp = true;
  235. CONTENTS;
  236. }
  237. $contents .= <<<CONTENTS
  238. protected static \$_table_name = '{$plural}';
  239. CONTENTS;
  240. $model = <<<MODEL
  241. <?php
  242. class Model_{$class_name} extends \Model_Crud
  243. {
  244. {$contents}
  245. }
  246. MODEL;
  247. }
  248. else
  249. {
  250. if ( ! \Cli::option('no-timestamp'))
  251. {
  252. $created_at = \Cli::option('created-at', 'created_at');
  253. is_string($created_at) or $created_at = 'created_at';
  254. $properties .= "\n\t\t'".$created_at."',";
  255. $updated_at = \Cli::option('updated-at', 'updated_at');
  256. is_string($updated_at) or $updated_at = 'updated_at';
  257. $properties .= "\n\t\t'".$updated_at."',";
  258. $time_type = (\Cli::option('mysql-timestamp')) ? 'timestamp' : 'int';
  259. $timestamp_properties = array($created_at.':'.$time_type.':null[1]', $updated_at.':'.$time_type.':null[1]');
  260. $args = array_merge($args, $timestamp_properties);
  261. }
  262. if ( ! \Cli::option('no-properties'))
  263. {
  264. $contents = <<<CONTENTS
  265. protected static \$_properties = array(
  266. {$properties}
  267. );
  268. CONTENTS;
  269. }
  270. if ( ! \Cli::option('no-timestamp'))
  271. {
  272. $mysql_timestamp = (\Cli::option('mysql-timestamp')) ? 'true' : 'false';
  273. if(($created_at = \Cli::option('created-at')) and is_string($created_at))
  274. {
  275. $created_at = <<<CONTENTS
  276. 'property' => '$created_at',
  277. CONTENTS;
  278. }
  279. else
  280. {
  281. $created_at = '';
  282. }
  283. if(($updated_at = \Cli::option('updated-at')) and is_string($updated_at))
  284. {
  285. $updated_at = <<<CONTENTS
  286. 'property' => '$updated_at',
  287. CONTENTS;
  288. }
  289. else
  290. {
  291. $updated_at = '';
  292. }
  293. $contents .= <<<CONTENTS
  294. protected static \$_observers = array(
  295. 'Orm\Observer_CreatedAt' => array(
  296. 'events' => array('before_insert'),
  297. 'mysql_timestamp' => $mysql_timestamp,$created_at
  298. ),
  299. 'Orm\Observer_UpdatedAt' => array(
  300. 'events' => array('before_save'),
  301. 'mysql_timestamp' => $mysql_timestamp,$updated_at
  302. ),
  303. );
  304. CONTENTS;
  305. }
  306. $model = <<<MODEL
  307. <?php
  308. class Model_{$class_name} extends \Orm\Model
  309. {
  310. {$contents}
  311. }
  312. MODEL;
  313. }
  314. // Build the model
  315. static::create($filepath, $model, 'model');
  316. if ( ! \Cli::option('no-migration'))
  317. {
  318. if ( ! empty($args))
  319. {
  320. array_unshift($args, 'create_'.$plural);
  321. static::migration($args, false);
  322. }
  323. else
  324. {
  325. throw new \Exception('Not enough arguments to create this migration.');
  326. }
  327. }
  328. $build and static::build();
  329. }
  330. public static function views($args, $subfolder, $build = true)
  331. {
  332. $controller = strtolower(array_shift($args));
  333. $controller_title = \Inflector::humanize($controller);
  334. $view_dir = APPPATH.'views/'.trim(str_replace(array('_', '-'), DS, $controller), DS).DS;
  335. $args or $args = array('index');
  336. // Make the directory for these views to be store in
  337. is_dir($view_dir) or static::$create_folders[] = $view_dir;
  338. // Add the default template if it doesnt exist
  339. if ( ! file_exists($app_template = APPPATH.'views/template.php'))
  340. {
  341. static::create($app_template, file_get_contents(\Package::exists('oil').'views/scaffolding/template.php'), 'view');
  342. }
  343. foreach ($args as $action)
  344. {
  345. $view_title = \Cli::option('with-viewmodel') ? '<?php echo $content; ?>' : \Inflector::humanize($action);
  346. $view = <<<VIEW
  347. <p>{$view_title}</p>
  348. VIEW;
  349. // Create this view
  350. static::create($view_dir.$action.'.php', $view, 'view');
  351. }
  352. $build and static::build();
  353. }
  354. public static function migration($args, $build = true)
  355. {
  356. // Get the migration name
  357. $migration_name = \Str::lower(str_replace(array('-', '/'), '_', array_shift($args)));
  358. if (empty($migration_name) or strpos($migration_name, ':'))
  359. {
  360. throw new Exception("Command is invalid.".PHP_EOL."\tphp oil g migration <migrationname> [<fieldname1>:<type1> |<fieldname2>:<type2> |..]");
  361. }
  362. // Check if a migration with this name already exists
  363. if (($duplicates = glob(APPPATH."migrations/*_{$migration_name}*")) === false)
  364. {
  365. throw new Exception("Unable to read existing migrations. Do you have an 'open_basedir' defined?");
  366. }
  367. if (count($duplicates) > 0)
  368. {
  369. // Don't override a file
  370. if (\Cli::option('s', \Cli::option('skip')) === true)
  371. {
  372. return;
  373. }
  374. // Tear up the file path and name to get the last duplicate
  375. $file_name = pathinfo(end($duplicates), PATHINFO_FILENAME);
  376. // Override the (most recent) migration with the same name by using its number
  377. if (\Cli::option('f', \Cli::option('force')) === true)
  378. {
  379. list($number) = explode('_', $file_name);
  380. }
  381. // Name clashes but this is done by hand. Assume they know what they're doing and just increment the file
  382. elseif (static::$scaffolding === false)
  383. {
  384. // Increment the name of this
  385. $migration_name = \Str::increment(substr($file_name, 4), 2);
  386. }
  387. }
  388. // See if the action exists
  389. $methods = get_class_methods(__NAMESPACE__ . '\Generate_Migration_Actions');
  390. // For empty migrations that dont have actions
  391. $migration = array('', '');
  392. // Loop through the actions and act on a matching action appropriately
  393. foreach ($methods as $method_name)
  394. {
  395. // If the miration name starts with the name of the action method
  396. if (substr($migration_name, 0, strlen($method_name)) === $method_name)
  397. {
  398. /**
  399. * Create an array of the subject the migration is about
  400. *
  401. * - In a migration named 'create_users' the subject is 'users' since thats what we want to create
  402. * So it would be the second object in the array
  403. * array(false, 'users')
  404. *
  405. * - In a migration named 'add_name_to_users' the object is 'name' and the subject is 'users'.
  406. * So again 'users' would be the second object, but 'name' would be the first
  407. * array('name', 'users')
  408. *
  409. */
  410. $subjects = array(false, false);
  411. $matches = explode('_', str_replace($method_name . '_', '', $migration_name));
  412. // create_{table}
  413. if (count($matches) == 1)
  414. {
  415. $subjects = array(false, $matches[0]);
  416. }
  417. // add_{field}_to_{table}
  418. else if (count($matches) == 3 && $matches[1] == 'to')
  419. {
  420. $subjects = array($matches[0], $matches[2]);
  421. }
  422. // delete_{field}_from_{table}
  423. else if (count($matches) == 3 && $matches[1] == 'from')
  424. {
  425. $subjects = array($matches[0], $matches[2]);
  426. }
  427. // rename_field_{field}_to_{field}_in_{table} (with underscores in field names)
  428. else if (count($matches) >= 5 && in_array('to', $matches) && in_array('in', $matches))
  429. {
  430. $subjects = array(
  431. implode('_', array_slice($matches, array_search('in', $matches)+1)),
  432. implode('_', array_slice($matches, 0, array_search('to', $matches))),
  433. implode('_', array_slice($matches, array_search('to', $matches)+1, array_search('in', $matches)-2))
  434. );
  435. }
  436. // rename_table
  437. else if ($method_name == 'rename_table')
  438. {
  439. $subjects = array(
  440. implode('_', array_slice($matches, 0, array_search('to', $matches))),
  441. implode('_', array_slice($matches, array_search('to', $matches)+1))
  442. );
  443. }
  444. // create_{table} or drop_{table} (with underscores in table name)
  445. else if (count($matches) !== 0)
  446. {
  447. $name = str_replace(array('create_', 'add_', '_to_'), array('create-', 'add-', '-to-'), $migration_name);
  448. if (preg_match('/^(create|add)\-([a-z0-9\_]*)(\-to\-)?([a-z0-9\_]*)?$/i', $name, $deep_matches))
  449. {
  450. switch ($deep_matches[1])
  451. {
  452. case 'create' :
  453. $subjects = array(false, $deep_matches[2]);
  454. break;
  455. case 'add' :
  456. $subjects = array($deep_matches[2], $deep_matches[4]);
  457. break;
  458. }
  459. }
  460. }
  461. // There is no subject here so just carry on with a normal empty migration
  462. else
  463. {
  464. break;
  465. }
  466. // We always pass in fields to a migration, so lets sort them out here.
  467. $fields = array();
  468. foreach ($args as $field)
  469. {
  470. $field_array = array();
  471. // Each paramater for a field is seperated by the : character
  472. $parts = explode(":", $field);
  473. // We must have the 'name:type' if nothing else!
  474. if (count($parts) >= 2)
  475. {
  476. $field_array['name'] = array_shift($parts);
  477. foreach ($parts as $part_i => $part)
  478. {
  479. preg_match('/([a-z0-9_-]+)(?:\[([0-9a-z\,\s]+)\])?/i', $part, $part_matches);
  480. array_shift($part_matches);
  481. if (count($part_matches) < 1)
  482. {
  483. // Move onto the next part, something is wrong here...
  484. continue;
  485. }
  486. $option_name = ''; // This is the name of the option to be passed to the action in a field
  487. $option = $part_matches;
  488. // The first option always has to be the field type
  489. if ($part_i == 0)
  490. {
  491. $option_name = 'type';
  492. $type = $option[0];
  493. if ($type === 'string')
  494. {
  495. $type = 'varchar';
  496. }
  497. else if ($type === 'integer')
  498. {
  499. $type = 'int';
  500. }
  501. if ( ! in_array($type, array('text', 'blob', 'datetime', 'date', 'timestamp', 'time')))
  502. {
  503. if ( ! isset($option[1]) || $option[1] == NULL)
  504. {
  505. if (isset(self::$_default_constraints[$type]))
  506. {
  507. $field_array['constraint'] = self::$_default_constraints[$type];
  508. }
  509. }
  510. else
  511. {
  512. // should support field_name:enum[value1,value2]
  513. if ($type === 'enum')
  514. {
  515. $values = explode(',', $option[1]);
  516. $option[1] = '"'.implode('","', $values).'"';
  517. $field_array['constraint'] = $option[1];
  518. }
  519. // should support field_name:decimal[10,2]
  520. elseif (in_array($type, array('decimal', 'float')))
  521. {
  522. $field_array['constraint'] = $option[1];
  523. }
  524. else
  525. {
  526. $field_array['constraint'] = (int) $option[1];
  527. }
  528. }
  529. }
  530. $option = $type;
  531. }
  532. else
  533. {
  534. // This allows you to put any number of :option or :option[val] into your field and these will...
  535. // ... always be passed through to the action making it really easy to add extra options for a field
  536. $option_name = array_shift($option);
  537. if (count($option) > 0)
  538. {
  539. $option = $option[0];
  540. }
  541. else
  542. {
  543. $option = true;
  544. }
  545. }
  546. // deal with some special cases
  547. switch ($option_name)
  548. {
  549. case 'auto_increment':
  550. case 'null':
  551. case 'unsigned':
  552. $option = (bool) $option;
  553. break;
  554. }
  555. $field_array[$option_name] = $option;
  556. }
  557. $fields[] = $field_array;
  558. }
  559. else
  560. {
  561. // Invalid field passed in
  562. continue;
  563. }
  564. }
  565. // Call the magic action which returns an array($up, $down) for the migration
  566. $migration = call_user_func(__NAMESPACE__ . "\Generate_Migration_Actions::{$method_name}", $subjects, $fields);
  567. }
  568. }
  569. // Build the migration
  570. list($up, $down)=$migration;
  571. $migration_name = ucfirst(strtolower($migration_name));
  572. $migration = <<<MIGRATION
  573. <?php
  574. namespace Fuel\Migrations;
  575. class {$migration_name}
  576. {
  577. public function up()
  578. {
  579. {$up}
  580. }
  581. public function down()
  582. {
  583. {$down}
  584. }
  585. }
  586. MIGRATION;
  587. $number = isset($number) ? $number : static::_find_migration_number();
  588. $filepath = APPPATH . 'migrations/'.$number.'_' . strtolower($migration_name) . '.php';
  589. static::create($filepath, $migration, 'migration');
  590. $build and static::build();
  591. }
  592. public static function task($args, $build = true)
  593. {
  594. if ( ! ($name = \Str::lower(array_shift($args))))
  595. {
  596. throw new Exception('No task name was provided.');
  597. }
  598. if (empty($args))
  599. {
  600. \Cli::write("\tNo tasks actions have been provided, the TASK will only create default task.", 'red');
  601. }
  602. $args or $args = array('index');
  603. // Uppercase each part of the class name and remove hyphens
  604. $class_name = \Inflector::classify($name, false);
  605. $filename = trim(str_replace(array('_', '-'), DS, $name), DS);
  606. $filepath = APPPATH.'tasks'.DS.$filename.'.php';
  607. $action_str = '';
  608. foreach ($args as $action)
  609. {
  610. $task_path = '\\'.\Inflector::humanize($name).'\\'.\Inflector::humanize($action);
  611. if (!ctype_alpha($action[0])) {
  612. throw new Exception('An action does not start with alphabet character. ABORTING');
  613. }
  614. $action_str .= '
  615. /**
  616. * This method gets ran when a valid method name is not used in the command.
  617. *
  618. * Usage (from command line):
  619. *
  620. * php oil r '.$name.':'.$action.' "arguments"
  621. *
  622. * @return string
  623. */
  624. public static function '.$action.'($args = NULL)
  625. {
  626. echo "\n===========================================";
  627. echo "\nRunning task ['.\Inflector::humanize($name).':'. \Inflector::humanize($action) . ']";
  628. echo "\n-------------------------------------------\n\n";
  629. /***************************
  630. Put in TASK DETAILS HERE
  631. **************************/
  632. }'.PHP_EOL;
  633. $message = \Cli::color("\t\tPreparing task method [", 'green');
  634. $message .= \Cli::color(\Inflector::humanize($action), 'cyan');
  635. $message .= \Cli::color("]", 'green');
  636. \Cli::write($message);
  637. }
  638. // Default RUN task action
  639. $action = 'run';
  640. $default_action_str = '
  641. /**
  642. * This method gets ran when a valid method name is not used in the command.
  643. *
  644. * Usage (from command line):
  645. *
  646. * php oil r '.$name.'
  647. *
  648. * @return string
  649. */
  650. public static function run($args = NULL)
  651. {
  652. echo "\n===========================================";
  653. echo "\nRunning DEFAULT task ['.\Inflector::humanize($name).':'. \Inflector::humanize($action) . ']";
  654. echo "\n-------------------------------------------\n\n";
  655. /***************************
  656. Put in TASK DETAILS HERE
  657. **************************/
  658. }'.PHP_EOL;
  659. // Build Controller
  660. $task_class = <<<CONTROLLER
  661. <?php
  662. namespace Fuel\Tasks;
  663. class {$class_name}
  664. {
  665. {$default_action_str}
  666. {$action_str}
  667. }
  668. /* End of file tasks/{$name}.php */
  669. CONTROLLER;
  670. // Write controller
  671. static::create($filepath, $task_class, 'tasks');
  672. $build and static::build();
  673. }
  674. public static function help()
  675. {
  676. $output = <<<HELP
  677. Usage:
  678. php oil [g|generate] [controller|model|migration|scaffold|views] [options]
  679. Runtime options:
  680. -f, [--force] # Overwrite files that already exist
  681. -s, [--skip] # Skip files that already exist
  682. -q, [--quiet] # Supress status output
  683. -t, [--speak] # Speak errors in a robot voice
  684. Description:
  685. The 'oil' command can be used to generate MVC components, database migrations
  686. and run specific tasks.
  687. Examples:
  688. php oil generate controller <controllername> [<action1> |<action2> |..]
  689. php oil g model <modelname> [<fieldname1>:<type1> |<fieldname2>:<type2> |..]
  690. php oil g migration <migrationname> [<fieldname1>:<type1> |<fieldname2>:<type2> |..]
  691. php oil g scaffold <modelname> [<fieldname1>:<type1> |<fieldname2>:<type2> |..]
  692. php oil g scaffold/template_subfolder <modelname> [<fieldname1>:<type1> |<fieldname2>:<type2> |..]
  693. php oil g config <filename> [<key1>:<value1> |<key2>:<value2> |..]
  694. Note that the next two lines are equivalent:
  695. php oil g scaffold <modelname> ...
  696. php oil g scaffold/crud <modelname> ...
  697. Documentation:
  698. http://docs.fuelphp.com/packages/oil/generate.html
  699. HELP;
  700. \Cli::write($output);
  701. }
  702. public static function create($filepath, $contents, $type = 'file')
  703. {
  704. $directory = dirname($filepath);
  705. is_dir($directory) or static::$create_folders[] = $directory;
  706. // Check if a file exists then work out how to react
  707. if (file_exists($filepath))
  708. {
  709. // Don't override a file
  710. if (\Cli::option('s', \Cli::option('skip')) === true)
  711. {
  712. // Don't bother trying to make this, carry on camping
  713. return;
  714. }
  715. // If we aren't skipping it, tell em to use -f
  716. if (\Cli::option('f', \Cli::option('force')) === null)
  717. {
  718. throw new Exception($filepath .' already exists, use -f or --force to override.');
  719. exit;
  720. }
  721. }
  722. static::$create_files[] = array(
  723. 'path' => $filepath,
  724. 'contents' => $contents,
  725. 'type' => $type
  726. );
  727. }
  728. public static function build()
  729. {
  730. foreach (static::$create_folders as $folder)
  731. {
  732. is_dir($folder) or mkdir($folder, 0755, TRUE);
  733. }
  734. foreach (static::$create_files as $file)
  735. {
  736. \Cli::write("\tCreating {$file['type']}: {$file['path']}", 'green');
  737. if ( ! $handle = @fopen($file['path'], 'w+'))
  738. {
  739. throw new Exception('Cannot open file: '. $file['path']);
  740. }
  741. $result = @fwrite($handle, $file['contents']);
  742. // Write $somecontent to our opened file.
  743. if ($result === false)
  744. {
  745. throw new Exception('Cannot write to file: '. $file['path']);
  746. }
  747. @fclose($handle);
  748. @chmod($file['path'], 0666);
  749. }
  750. return $result;
  751. }
  752. public static function class_name($name)
  753. {
  754. return str_replace(array(' ', '-'), '_', ucwords(str_replace('_', ' ', $name)));
  755. }
  756. // Helper methods
  757. private static function _find_migration_number()
  758. {
  759. $glob = glob(APPPATH .'migrations/*_*.php');
  760. list($last) = explode('_', basename(end($glob)));
  761. return str_pad($last + 1, 3, '0', STR_PAD_LEFT);
  762. }
  763. private static function _update_current_version($version)
  764. {
  765. if (file_exists($app_path = APPPATH.'config'.DS.'migrations.php'))
  766. {
  767. $contents = file_get_contents($app_path);
  768. }
  769. elseif (file_exists($core_path = COREPATH.'config'.DS.'migrations.php'))
  770. {
  771. $contents = file_get_contents($core_path);
  772. }
  773. else
  774. {
  775. throw new \Exception('Config file core/config/migrations.php');
  776. exit;
  777. }
  778. $contents = preg_replace("#('version'[ \t]+=>)[ \t]+([0-9]+),#i", "$1 $version,", $contents);
  779. static::create($app_path, $contents, 'config');
  780. }
  781. }
  782. /* End of file oil/classes/generate.php */