PageRenderTime 69ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/console.php

https://github.com/Fusion/lenses
PHP | 758 lines | 708 code | 40 blank | 10 comment | 105 complexity | 307727e6f65eabb987c59d40f4efe8fd MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * @package Lenses
  4. * @copyright (c) Chris F. Ravenscroft
  5. * @license See 'license.txt'
  6. */
  7. global $WHEREAMI, $medium;
  8. $postinput = '';
  9. if(empty($_ENV['SHELL']))
  10. {
  11. // Web invocation
  12. $WHEREAMI = dirname(getcwd());
  13. include($WHEREAMI.'/config.php');
  14. $ip = $_SERVER['REMOTE_ADDR'];
  15. if(!Config::$webcli || !in_array($ip, Config::$webcliips))
  16. die("Sorry, command-line only!");
  17. $medium = 'w';
  18. $prompt = '<body onload="document.getElementById(\'input\').focus();"><form method="post" action="console.php">Console > <input type="text" name="input" id="input" value="" style="width:800px;" /></form>';
  19. if(!empty($_POST['input']))
  20. $postinput = $_POST['input'];
  21. }
  22. else
  23. {
  24. // Shell
  25. if(empty($argv[1]))
  26. die("Use 'please' rather than invoking this script directly!\n");
  27. $WHEREAMI = $argv[1];
  28. include($WHEREAMI.'/config.php');
  29. $medium = 'c';
  30. $prompt = 'Console > ';
  31. }
  32. $cli = array(
  33. 'halp' => 'lolcat',
  34. 'create' => array(
  35. 'model' => array(
  36. '-1' => 'create model [context] <name>',
  37. '1' => "create_model",
  38. '2' => "create_model"),
  39. 'controller' => array(
  40. '-1' => 'create controller [context] <name>',
  41. '1' => "create_controller",
  42. '2' => "create_controller"),
  43. 'view' => array(
  44. '-1' => 'create view [context] <name> <page>',
  45. '2' => "create_view",
  46. '3' => "create_view"),
  47. 'mvc' => array(
  48. '-1' => 'create mvc [context] <name>',
  49. '1' => "create_mvc",
  50. '2' => "create_mvc"),
  51. 'helper' => array(
  52. '-1' => 'create helper <name>',
  53. '1' => "create_helper"),
  54. 'setting' => array(
  55. '-1' => 'create setting <name> <value> <type> <options> <group> "<description>"',
  56. '6' => "create_setting"),
  57. 'test' => array(
  58. 'awesome' => 1,
  59. ),
  60. ),
  61. 'delete' => array(
  62. 'setting' => array(
  63. '-1' => 'delete setting <name>',
  64. '1' => "delete_setting"),
  65. ),
  66. 'set' => array(
  67. 'setting' => array(
  68. '-1' => 'set setting <name> <value>',
  69. '2' => "set_setting"),
  70. ),
  71. 'show' => array(
  72. 'settings' => array(
  73. '0' => "show_settings"),
  74. 'setting' => array(
  75. '-1' => 'show setting <prefix>',
  76. '1' => "show_setting"),
  77. ),
  78. 'migrate' => array(
  79. 'up' => array(
  80. '-1' => 'migrate up [version]',
  81. '0' => "migrate_model_up",
  82. '1' => "migrate_model_up"),
  83. 'down' => array(
  84. '-1' => 'migrate down <version>',
  85. '1' => "migrate_model_down"),
  86. ),
  87. );
  88. $depth = 0;
  89. $innerWelcome = ($medium == 'w' ? '' : "Type '.' to exit.");
  90. echo _format("Welcome to the Console. $innerWelcome\n");
  91. $stdin = fopen('php://stdin', 'r');
  92. echo _format($prompt);
  93. $tokens = array();
  94. $incomplete = false;
  95. while(($line = fgets($stdin)) || !empty($postinput))
  96. {
  97. if(!empty($postinput))
  98. {
  99. $line = $postinput;
  100. $postinput = '';
  101. }
  102. $probe = &$cli;
  103. $collectingArgs = false;
  104. $args = array();
  105. $line = str_replace(array("\n", "\r"), array('', ''), $line);
  106. if(empty($line))
  107. {
  108. $incomplete = false;
  109. echo _format($prompt);
  110. continue;
  111. }
  112. if($line == '.') break;
  113. $input = explode(' ', $line);
  114. if($incomplete)
  115. {
  116. if(empty($line))
  117. {
  118. $input = $tokens;
  119. }
  120. else
  121. {
  122. $input = array_merge($tokens, $input);
  123. }
  124. }
  125. $tokens = array();
  126. $incomplete = true;
  127. $curArgStr = '';
  128. foreach($input as $token)
  129. {
  130. if($collectingArgs)
  131. {
  132. if(empty($curArgStr))
  133. {
  134. if($token{0} == '"')
  135. $curArgStr = substr($token, 1);
  136. else
  137. $args[] = $token;
  138. }
  139. else
  140. {
  141. if($token{strlen($token)-1} == '"')
  142. {
  143. $args[] = $curArgStr.' '.substr($token, 0, strlen($token)-1);
  144. $curArgStr = '';
  145. }
  146. else
  147. $curArgStr .= ' '.$token;
  148. }
  149. }
  150. else if(isset($probe[$token]))
  151. {
  152. $tokens[] = $token;
  153. $probe = &$probe[$token];
  154. if(!is_array($probe))
  155. {
  156. $arrptr = array($probe);
  157. $incomplete = false;
  158. $collectingArgs = true;
  159. }
  160. else
  161. {
  162. $keys = array_keys($probe);
  163. if(is_int($keys[0]))
  164. {
  165. $arrptr = &$probe;
  166. $incomplete = false;
  167. $collectingArgs = true;
  168. }
  169. }
  170. }
  171. else
  172. {
  173. echo _format("Syntax error: {$token}?\n");
  174. break;
  175. }
  176. }
  177. if($incomplete)
  178. {
  179. $comma = '';
  180. echo _format("Syntax: " . implode(' ', $tokens) . " < ");
  181. foreach($probe as $potentialToken => $whatever)
  182. {
  183. echo _format("$comma$potentialToken");
  184. $comma = ' | ';
  185. }
  186. echo _format(" >\n");
  187. }
  188. else if($collectingArgs)
  189. {
  190. $idx = count($args);
  191. if(!empty($arrptr[$idx]))
  192. {
  193. $fn = $arrptr[$idx];
  194. $fn($args);
  195. }
  196. else
  197. {
  198. echo _format("Wrong number of arguments: ".$arrptr[-1]."\n");
  199. $incomplete = true;
  200. }
  201. }
  202. if($medium != 'w')
  203. echo $prompt;
  204. if($incomplete)
  205. {
  206. if(0 < count($tokens))
  207. $value = implode(' ', $tokens) . ' ';
  208. else
  209. $value = '';
  210. if($medium != 'w')
  211. echo $value;
  212. else
  213. echo "<script>\ndocument.getElementById('input').value = '$value';\n</script>\n";
  214. }
  215. }
  216. if($medium != 'w')
  217. echo _format("Good bye!\n");
  218. function _format($str)
  219. {
  220. global $medium;
  221. if($medium == 'w')
  222. $str = str_replace("\n", "<br />\n", $str);
  223. return $str;
  224. }
  225. function _ensure_context($contextname)
  226. {
  227. global $WHEREAMI;
  228. if(file_exists($WHEREAMI.'/app/'.$contextname))
  229. {
  230. if(is_dir($WHEREAMI.'/app/'.$contextname))
  231. return true;
  232. echo _format("This context exists but it's a file, not a directory!\n");
  233. return false;
  234. }
  235. if(
  236. !mkdir($WHEREAMI.'/app/'.$contextname) ||
  237. !mkdir($WHEREAMI.'/app/'.$contextname.'/models') ||
  238. !mkdir($WHEREAMI.'/app/'.$contextname.'/controllers') ||
  239. !mkdir($WHEREAMI.'/app/'.$contextname.'/views')
  240. )
  241. {
  242. echo _format("Problem creating context '$contextname'\n");
  243. return false;
  244. }
  245. return true;
  246. }
  247. function _create_file($filename, $contents='')
  248. {
  249. global $WHEREAMI;
  250. $filename = $WHEREAMI.'/'.$filename;
  251. $f = fopen($filename, 'w+');
  252. if(!$f)
  253. {
  254. echo _format("Unable to create $filename\n");
  255. return false;
  256. }
  257. if(!fputs($f, $contents))
  258. {
  259. echo _format("Unable to create contents of $filename\n");
  260. return false;
  261. }
  262. fclose($f);
  263. return true;
  264. }
  265. function _open_db()
  266. {
  267. global $db, $WHEREAMI;
  268. if(!isset($db))
  269. {
  270. include($WHEREAMI.'/libs/adodb/adodb.inc.php');
  271. $db = NewADOConnection(
  272. Config::$dbengine.'://'.
  273. Config::$dbuser.':'.
  274. Config::$dbpassword.'@'.
  275. Config::$dbhost.'/'.
  276. Config::$dbname);
  277. }
  278. if(!$db)
  279. {
  280. echo _format("Unable to access database. Bad configuration in config.php?\n");
  281. return false;
  282. }
  283. return true;
  284. }
  285. function _create_dict()
  286. {
  287. global $db, $dict, $WHEREAMI;
  288. if(!isset($dict))
  289. {
  290. include($WHEREAMI.'/libs/adodb/adodb-datadict.inc.php');
  291. $dict = NewDataDictionary($db);
  292. }
  293. return $dict;
  294. }
  295. function _create_yml_parser()
  296. {
  297. global $parserLoaded, $WHEREAMI;
  298. if(!isset($parserLoaded))
  299. {
  300. include($WHEREAMI.'/libs/spyc/spyc-php5.php');
  301. $parserLoaded = true;
  302. }
  303. }
  304. function lolcat($args)
  305. {
  306. echo _format("Type '?' for a short syntax help message.\n");
  307. }
  308. function create_model($args)
  309. {
  310. if(empty($args)) { echo _format("Argument please! ([contextname] <modelname>)\n"); return false; }
  311. if(count($args)>1)
  312. {
  313. $contextname = $args[0];
  314. if(!_ensure_context($contextname)) return false;
  315. $partname = $args[1];
  316. $filename = 'app/'.$contextname.'/models/'.$partname.'.php';
  317. }
  318. else
  319. {
  320. $partname = $args[0];
  321. $filename = 'models/'.$partname.'.php';
  322. }
  323. $classname = ucfirst($partname);
  324. $contents = <<<EOB
  325. <?php
  326. class {$classname} extends ActiveRecord
  327. {
  328. function __construct()
  329. {
  330. parent::__construct('{$partname}');
  331. }
  332. }
  333. ?>
  334. EOB;
  335. if(!_create_file($filename, $contents)) return false;
  336. echo _format("Model: Success.\n");
  337. return true;
  338. }
  339. function create_controller($args)
  340. {
  341. if(empty($args)) { echo _format("Argument please! ([contextname] <controllername>)\n"); return false; }
  342. if(count($args)>1)
  343. {
  344. $contextname = $args[0];
  345. if(!_ensure_context($contextname)) return false;
  346. $partname = $args[1];
  347. $filename = 'app/'.$contextname.'/controllers/'.$partname.'_controller.php';
  348. }
  349. else
  350. {
  351. $partname = $args[0];
  352. $filename = 'controllers/'.$partname.'_controller.php';
  353. }
  354. $classname = ucfirst($partname).'Controller';
  355. $contents = <<<EOB
  356. <?php
  357. class {$classname} extends ApplicationController
  358. {
  359. function index()
  360. {
  361. }
  362. }
  363. ?>
  364. EOB;
  365. if(!_create_file($filename, $contents)) return false;
  366. echo _format("Controller: Success.\n");
  367. return true;
  368. }
  369. function create_view($args)
  370. {
  371. if(empty($args)) { echo _format("Argument please! ([contextname] <viewname> <pagename>)\n"); return false; }
  372. if(count($args)>2)
  373. {
  374. $contextname = $args[0];
  375. if(!_ensure_context($contextname)) return false;
  376. $viewname = $args[1];
  377. $pagename = $args[2];
  378. $pathname = 'app/'.$contextname.'/views/'.$viewname;
  379. }
  380. else
  381. {
  382. $viewname = $args[0];
  383. $pagename = $args[1];
  384. $pathname = 'views/'.$viewname;
  385. }
  386. if(!mkdir($pathname)) { echo _format("Problem creating view '$pathname'\n"); return false; }
  387. $contents = <<<EOB
  388. <?php
  389. if(\$message_type!=MESSAGE_ERROR):
  390. ?>
  391. <?php
  392. endif
  393. ?>
  394. EOB;
  395. if(!_create_file($pathname.'/'.$pagename.'.html.php', $contents)) return false;
  396. echo _format("View: Success.\n");
  397. return true;
  398. }
  399. function create_mvc($args)
  400. {
  401. if(!create_model($args)) return false;
  402. if(!create_controller($args)) return false;
  403. array_push($args, 'index');
  404. if(!create_view($args)) return false;
  405. echo _format("All created.\n");
  406. return true;
  407. }
  408. function create_setting($args)
  409. {
  410. global $db;
  411. if(!_open_db()) return false;
  412. list($name, $value, $type, $options, $group, $description) = $args;
  413. $qry = "INSERT INTO settings(`name`, `value`, `type`, `options`, `group`, `description`) VALUES('".
  414. addslashes($name)."', '".
  415. addslashes($value)."', '".
  416. addslashes(strtoupper($type))."', '".
  417. addslashes($options)."', '".
  418. intval($group)."', '".
  419. addslashes($description)."')";
  420. if(!$db->execute($qry))
  421. {
  422. echo _format("Unable to create setting '$name'.\n");
  423. return false;
  424. }
  425. echo _format("Setting '$name' created.\n");
  426. return true;
  427. }
  428. function delete_setting($args)
  429. {
  430. global $db;
  431. $name = $args[0];
  432. if(!_open_db()) return false;
  433. $qry = "DELETE FROM settings WHERE name='{$name}'";
  434. if(!$db->execute($qry))
  435. {
  436. echo _format("Unable to delete setting '$name'.\n");
  437. return false;
  438. }
  439. echo _format("Setting '$name' deleted.\n");
  440. return true;
  441. }
  442. function show_settings()
  443. {
  444. return show_setting();
  445. }
  446. function show_setting($args = null)
  447. {
  448. global $db;
  449. if(!_open_db()) return false;
  450. $qry = 'SELECT * FROM settings';
  451. if(!empty($args)) $qry .= " WHERE `name` like '{$args[0]}%'";
  452. $rs = &$db->execute($qry);
  453. echo _format("\n");
  454. while(!$rs->EOF)
  455. {
  456. echo _format("Description:\t".$rs->fields['description']."\n".
  457. "Name:\t\t".$rs->fields['name']."\n".
  458. "Value:\t\t".$rs->fields['value']."\n\n");
  459. $rs->moveNext();
  460. }
  461. return true;
  462. }
  463. function _m_check_table($table_name)
  464. {
  465. global $db;
  466. if(!_open_db()) return false;
  467. $qry = "SELECT id FROM $table_name";
  468. $rs = &$db->execute($qry);
  469. return (false !== $rs);
  470. }
  471. function _m_execute($qry)
  472. {
  473. global $db;
  474. if(!_open_db()) return false;
  475. if(!$db->execute($qry))
  476. {
  477. echo _format("! Error executing '$qry'\n");
  478. return false;
  479. }
  480. return true;
  481. }
  482. function _m_execute_multi($qries)
  483. {
  484. foreach($qries as $qry)
  485. {
  486. if(!_m_execute($qry))
  487. return false;
  488. }
  489. return true;
  490. }
  491. function _m_query($qry)
  492. {
  493. global $db;
  494. if(!_open_db()) return false;
  495. $rs = &$db->execute($qry);
  496. if(!$rs)
  497. {
  498. echo _format("! Error executing '$qry'\n");
  499. return false;
  500. }
  501. $ret = array();
  502. while(!$rs->EOF)
  503. {
  504. $ret[] = $rs->fields;
  505. $rs->moveNext();
  506. }
  507. return $ret;
  508. }
  509. function _m_create_table($table_name, $field_defs)
  510. {
  511. global $db, $dict;
  512. if(!_open_db() || !_create_dict()) return false;
  513. $sql = $dict->CreateTableSQL($table_name, $field_defs, array());
  514. if(!$sql)
  515. {
  516. echo _format("! Error creating table '$table_name'\n");
  517. return false;
  518. }
  519. foreach($sql as $qry)
  520. {
  521. if(!$db->execute($qry))
  522. {
  523. echo _format("! Error creating table '$table_name':\n\"$qry\"\n");
  524. return false;
  525. }
  526. }
  527. print "Created table '$table_name'\n";
  528. return true;
  529. }
  530. function _m_drop_table($table_name)
  531. {
  532. global $db, $dict;
  533. if(!_open_db() || !_create_dict()) return false;
  534. $sql = $dict->DropTableSQL($table_name);
  535. if(!$sql)
  536. {
  537. echo _format("! Error dropping table '$table_name'\n");
  538. return false;
  539. }
  540. foreach($sql as $qry)
  541. {
  542. if(!$db->execute($qry))
  543. {
  544. echo _format("! Error dropping table '$table_name':\n\"$qry\"\n");
  545. return false;
  546. }
  547. }
  548. print "Dropped table '$table_name'\n";
  549. return true;
  550. }
  551. function _m_error()
  552. {
  553. throw new Exception('Migration problem');
  554. }
  555. function _migrate($mo)
  556. {
  557. if(!empty($mo['drop']))
  558. {
  559. foreach($mo['drop'] as $row)
  560. {
  561. if(!_m_drop_table($row['name']))
  562. _m_error();
  563. }
  564. }
  565. if(!empty($mo['create']))
  566. {
  567. foreach($mo['create'] as $row)
  568. {
  569. if(!_m_create_table(
  570. $row['name'],
  571. $row['info']))
  572. _m_error();
  573. }
  574. }
  575. if(!empty($mo['execute']))
  576. {
  577. foreach($mo['execute'] as $row)
  578. {
  579. if(!_m_execute($row['query']))
  580. _m_error();
  581. }
  582. }
  583. }
  584. function _prepare_to_migrate()
  585. {
  586. // First, does system table exist?
  587. if(!_m_check_table('system'))
  588. {
  589. if(!_m_create_table(
  590. 'system',
  591. "
  592. id I AUTO KEY,
  593. setting VARCHAR(32) INDEX setting NOTNULL,
  594. value VARCHAR(64) NOTNULL
  595. "))
  596. return false;
  597. if(!_m_execute("INSERT INTO `system`(`setting`,`value`) VALUES('version', 0)"))
  598. return false;
  599. }
  600. $row = _m_query("SELECT * FROM `system` WHERE `setting`='version'");
  601. if(count($row) != 1)
  602. {
  603. echo _format("! Wrong row number when reading version from system table\n");
  604. return false;
  605. }
  606. $curVersion = intval($row[0]['value']);
  607. if(0 > $curVersion)
  608. {
  609. echo _format("Sorry, but it appears that the database got corrupted while trying to migrate to version ".
  610. (-1 * $curVersion).".\nThe database needs to be fixed before any new migration.\n");
  611. return false;
  612. }
  613. return $curVersion;
  614. }
  615. function migrate_model_down($args = null)
  616. {
  617. global $WHEREAMI;
  618. $targetVersion = intval($args[0]);
  619. if(0 > $targetVersion)
  620. {
  621. echo _format("Wrong parameter for version number\n");
  622. return false;
  623. }
  624. $curVersion = _prepare_to_migrate();
  625. if(false === $curVersion)
  626. return false;
  627. if($curVersion <= $targetVersion)
  628. {
  629. echo _format("Nothing to do: current version=$curVersion, target version=$targetVersion\n");
  630. return true;
  631. }
  632. $nextVersion = $curVersion - 1;
  633. try
  634. {
  635. while($nextVersion >= $targetVersion)
  636. {
  637. $fName = $WHEREAMI . '/migrations/' . sprintf('%03d', ($nextVersion + 1)) . '.yml';
  638. if(!file_exists($fName))
  639. break;
  640. echo _format("----------------------------------------\n");
  641. echo _format("Migrating from version $curVersion to version $nextVersion\n");
  642. echo _format("----------------------------------------\n");
  643. _create_yml_parser();
  644. $arr = Spyc::YAMLLoad($fName);
  645. $down = &$arr['down'];
  646. _migrate($down);
  647. unset($arr);
  648. $curVersion = $nextVersion;
  649. $nextVersion --;
  650. }
  651. }
  652. catch(Exception $e)
  653. {
  654. // Oh no I failed! Store wannabe version number...with a twist: it's negative!
  655. _m_execute("UPDATE `system` SET `value`=".(-1 * $nextVersion)." WHERE `setting`='version'");
  656. echo _format("Alas, there was an issue migrating from #$curVersion to $nextVersion!\n");
  657. return false;
  658. }
  659. _m_execute("UPDATE `system` SET `value`=".($nextVersion + 1)." WHERE `setting`='version'");
  660. echo _format("Database fully migrated to version ".($nextVersion + 1).".\n");
  661. return true;
  662. }
  663. function migrate_model_up($args = null)
  664. {
  665. global $WHEREAMI;
  666. if(!empty($args) && !empty($args[0]))
  667. $targetVersion = intval($args[0]);
  668. else
  669. $targetVersion = 999999;
  670. if(0 >= $targetVersion)
  671. {
  672. echo _format("Wrong parameter for version number\n");
  673. return false;
  674. }
  675. $curVersion = _prepare_to_migrate();
  676. if(false === $curVersion)
  677. return false;
  678. if($curVersion >= $targetVersion)
  679. {
  680. echo _format("Nothing to do: current version=$curVersion, target version=$targetVersion\n");
  681. return true;
  682. }
  683. $nextVersion = $curVersion + 1;
  684. try
  685. {
  686. while($nextVersion <= $targetVersion)
  687. {
  688. $fName = $WHEREAMI . '/migrations/' . sprintf('%03d', $nextVersion) . '.yml';
  689. if(!file_exists($fName))
  690. break;
  691. echo _format("----------------------------------------\n");
  692. echo _format("Migrating from version $curVersion to version $nextVersion\n");
  693. echo _format("----------------------------------------\n");
  694. _create_yml_parser();
  695. $arr = Spyc::YAMLLoad($fName);
  696. $up = &$arr['up'];
  697. _migrate($up);
  698. unset($arr);
  699. $curVersion = $nextVersion;
  700. $nextVersion ++;
  701. }
  702. }
  703. catch(Exception $e)
  704. {
  705. // Oh no I failed! Store wannabe version number...with a twist: it's negative!
  706. _m_execute("UPDATE `system` SET `value`=".(-1 * $nextVersion)." WHERE `setting`='version'");
  707. echo _format("Alas, there was an issue migrating from #$curVersion to $nextVersion!\n");
  708. return false;
  709. }
  710. _m_execute("UPDATE `system` SET `value`=".($nextVersion - 1)." WHERE `setting`='version'");
  711. echo _format("\n# Database fully migrated to version ".($nextVersion - 1).".\n");
  712. return true;
  713. }
  714. ?>