PageRenderTime 70ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/atk4/lib/ApiCLI.php

https://github.com/mahimarathore/mahi
PHP | 489 lines | 222 code | 32 blank | 235 comment | 34 complexity | 38d9f8d8d479d05bd64c122240428844 MD5 | raw file
Possible License(s): AGPL-3.0, MPL-2.0-no-copyleft-exception
  1. <?php // vim:ts=4:sw=4:et:fdm=marker
  2. /**
  3. ==ATK4===================================================
  4. This file is part of Agile Toolkit 4
  5. http://agiletoolkit.org/
  6. (c) 2008-2013 Agile Toolkit Limited <info@agiletoolkit.org>
  7. Distributed under Affero General Public License v3 and
  8. commercial license.
  9. See LICENSE or LICENSE_COM for more information
  10. =====================================================ATK4=*/
  11. /**
  12. * Base class for Command-Line Applications. The purpose of Application class
  13. * is to initialize all the other classes and aid their connectivity. API
  14. * class can be accessed from any object through $this->api property.
  15. *
  16. * API classes are derrived from AbstractView because normally they would have
  17. * a template and will be able to render themselves consistently to any other
  18. * view in the system. Although ApiCLI does not do any rendering, it's descendants
  19. * do
  20. *
  21. * @link http://agiletoolkit.org/doc/api
  22. */
  23. class ApiCLI extends AbstractView
  24. {
  25. /**
  26. * In a typical application, one connection to the database is enough for
  27. * majority of applications. Calling $api->dbConnect will read Database
  28. * data from config file and store it in $db property. If you requires
  29. * a more advanced connectivity or multiple connections, you can manually
  30. * initialize more database connections.
  31. *
  32. * @see dbConnect()
  33. */
  34. public $db=null;
  35. /**
  36. * ApiCLI implements a API for accessing your application configuration.
  37. * Once configuration file is read, data is saved inside this property.
  38. *
  39. * @see getConfig()
  40. * @see readConfig()
  41. */
  42. protected $config = null;
  43. /**
  44. * Without logger, API will dump out errors and exceptions in a very brief
  45. * and straigtforward way. Logger is a controller which enhances error
  46. * output and in most cases you do need one. Logger can be further configured
  47. * to either output detailed errors or show brief message instead.
  48. *
  49. * @see Logger
  50. */
  51. public $logger=null;
  52. /**
  53. * If you want to use your own logger class, redefine this property
  54. */
  55. public $logger_class='Logger';
  56. /**
  57. * PathFinder is a controller which is responsible for locating resources,
  58. * such as PHP includes, JavaScript files, templates, etc. API Initializes
  59. * PathFinder as soon as possible, then defines "Locations" which describe
  60. * type of data found in different folders.
  61. */
  62. public $pathfinder=null;
  63. /**
  64. * If you would want to use your own PathFinder class, you must change
  65. * this property and include it.
  66. */
  67. protected $pathfinder_class='PathFinder';
  68. /**
  69. * This is a major version of Agile Toolkit. The API of Agile Toolkit is
  70. * very well established and changes rarely. Your application would generally
  71. * be compatible throughout the same major version of Agile Tooolkit.
  72. *
  73. * @see requires();
  74. */
  75. public $atk_version=4.2;
  76. /**
  77. * Some Agile Toolkit classes contain references to profiler. Profiler
  78. * would be initialized early and reference would be kept in this variable.
  79. * Profiler measures relative time it took in certain parts of your
  80. * application to help you find a slow-perfoming parts of application.
  81. *
  82. * By default $pr points to empty profiler object, which implements empty
  83. * methods. All the lines referencing $pr myst be prefixed with the
  84. * 4-symbol sequence "/ ** /" (no spaces). If you want to speed up Agile
  85. * Toolkit further, you can eliminate all lines started with this sequence
  86. * from your source code.
  87. */
  88. /**/public $pr;
  89. /**
  90. * Object in Agile Toolkit contain $name property which is derrived from
  91. * the owher object and keeps extending as you add objects deeper into
  92. * run-time tree. Sometimes that may generate long names. Long names are
  93. * difficult to read, they increase HTML output size but most importantly
  94. * they may be restricted by security extensions such as SUHOSIN.
  95. *
  96. * Agile Toolkit implements a mechanism which will replace common beginning
  97. * of objects with an abbreviation thus keeping object name length under
  98. * control. This variable defines the maximum length of the object's $name.
  99. * Be mindful that some objects will concatinate theri name with fields,
  100. * so the maximum letgth of GET argument names can exceed this value by
  101. * the length of your field.
  102. *
  103. * We recommend you to increase SUHOSIN get limits if you encounter any
  104. * problems. Set this value to "false" to turn off name shortening.
  105. */
  106. public $max_name_length=60;
  107. /**
  108. * As more names are shortened, the substituted part is being placed into
  109. * this hash and the value contains the new key. This helps to avoid creating
  110. * many sequential prefixes for the same character sequenece.
  111. */
  112. public $unique_hashes=array();
  113. // {{{ Start-up of application
  114. /**
  115. * Regular objects in Agile Toolkit use init() and are added through add().
  116. * Application class is differente, you use "new" keyword because it's the
  117. * first class to be created. That's why constructor will perform quite a
  118. * bit of initialization.
  119. *
  120. * Do not redefine constructor but instead use init();
  121. *
  122. * $realm defines a top-level name of your application. This impacts all
  123. * id= prefixes in your HTML code, form field names and many other things,
  124. * such as session name. If you have two application classes which are part
  125. * of same web app and may want to use same realm, but in other cases it's
  126. * preferably that you keep realm unique on your domain in the interests
  127. * of security.
  128. *
  129. * @param string $realm Will become $api->name
  130. */
  131. function __construct($realm = null)
  132. {
  133. if (!$realm) {
  134. $realm=get_class($this);
  135. }
  136. $this->owner = $this;
  137. $this->name = $realm;
  138. $this->api = $this;
  139. // Profiler is a class for benchmarking your application. All calls to pr
  140. /**/$this->pr=new Dummy();
  141. try {
  142. $this->add($this->pathfinder_class);
  143. $this->init();
  144. } catch (Exception $e) {
  145. // This exception is used to abort initialisation of the objects but when
  146. // normal rendering is still required
  147. if ($e instanceof Exception_StopInit) {
  148. return;
  149. }
  150. // Handles output of the exception
  151. $this->caughtException($e);
  152. }
  153. }
  154. // }}}
  155. // {{{ Management of Global Methods
  156. /**
  157. * Agile Toolkit objects allow method injection. This is quite similar
  158. * to technique used in JavaScript:
  159. *
  160. * obj.test = function() { .. }
  161. *
  162. * All non-existant method calls on all Agile Toolkit objects will be
  163. * tried against local table of registered methods and then against
  164. * global registered methods.
  165. *
  166. * addGlobalmethod allows you to register a globally-recognized for all
  167. * agile toolkit object. PHP is not particularly fast about executing
  168. * methods like that, but this technique can be used for adding
  169. * backward-compatibility or debugging, etc.
  170. *
  171. * @param string $name Name of the method
  172. * @param callable $callable Calls your function($object, $arg1, $arg2)
  173. *
  174. * @see AbstractObject::hasMethod()
  175. * @see AbstractObject::__call()
  176. *
  177. * @return void
  178. */
  179. function addGlobalMethod($name, $callable)
  180. {
  181. if ($this->hasMethod($name)) {
  182. throw $this->exception('Registering method twice')
  183. ->addMoreInfo('name', $name);
  184. }
  185. $this->addHook('global-method-'.$name, $callable);
  186. }
  187. /**
  188. * Returns if a global method with such name was defined
  189. *
  190. * @param string $name Name of the method
  191. *
  192. * @return boolean if registered
  193. */
  194. function hasGlobalMethod($name){
  195. return isset($this->hooks['global-method-'.$name]);
  196. }
  197. /** Removes global method */
  198. function removeGlobalMethod($name){
  199. $this->removeHook('global-method-'.$name);
  200. }
  201. // }}}
  202. // {{{ Localization
  203. /** Redefine this function to introduce your localization. Agile Toolkit will pass all system strings
  204. * through this method. If some methods are not properly passed through, please fork Agile Toolkit in
  205. * http://github.com/atk4/atk4/ , modify, commit, push your fix and notify authors of Agile Toolkit
  206. * using contact form on http://agiletoolkit.org/contact
  207. *
  208. * See file CONTRIBUTING
  209. */
  210. function _($str){
  211. $x=$this->hook('localizeString',array($str));
  212. if($x)return $x[0];
  213. return $str;
  214. }
  215. // }}}
  216. // {{{ PathFinder and PageManager bindings
  217. /** Find relative path to the resource respective to the current directory. */
  218. function locate($type,$filename='',$return='relative'){
  219. return $this->pathfinder->locate($type,$filename,$return);
  220. }
  221. /** Calculate URL pointing to specified resource */
  222. function locateURL($type,$filename=''){
  223. return $this->pathfinder->locate($type,$filename,'url');
  224. }
  225. /** Return full system path to specified resource */
  226. function locatePath($type,$filename=''){
  227. return $this->pathfinder->locate($type,$filename,'path');
  228. }
  229. /** Add new location with additional resources */
  230. function addLocation($location,$contents){
  231. return $this->pathfinder->addLocation($location,$contents);
  232. }
  233. /** Returns base URL of this Web application installation. If you require
  234. * link to a page, you can use URL::useAbsoluteURL();
  235. *
  236. * @see URL::useAbsoluteURL() */
  237. function getBaseURL(){
  238. return $this->pm->base_path;
  239. }
  240. /** Generates URL for specified page. Useful for building links on pages or emails. Returns URL object. */
  241. function url($page=null,$arguments=array()){
  242. if(is_object($page) && $page instanceof URL){
  243. // we receive URL
  244. return $page->setArguments($arguments);
  245. }
  246. $url=$this->add('URL','url_'.$this->url_object_count++);
  247. unset($this->elements[$url->short_name]); // garbage collect URLs
  248. if(strpos($page,'http://')===0 || strpos($page,'https://')===0) $url->setURL($page);
  249. else $url->setPage($page);
  250. return $url->setArguments($arguments);
  251. }
  252. /** @obsolete use url() */
  253. function getDestinationURL($page=null,$arguments=array()){ return $this->url($page,$arguments); }
  254. // }}}
  255. // {{{ Error handling
  256. /** Initialize logger or return existing one */
  257. function getLogger($class_name=undefined){
  258. if(is_null($this->logger)){
  259. $this->logger=$this->add($class_name===undefined?$this->logger_class:$class_name);
  260. }
  261. return $this->logger;
  262. }
  263. /** Is executed if exception is raised during execution. Re-define to have custom handling of exceptions system-wide */
  264. function caughtException($e){
  265. $this->hook('caught-exception',array($e));
  266. echo get_class($e),": ".$e->getMessage();
  267. exit;
  268. }
  269. /** @obsolete */
  270. function outputWarning($msg,$shift=0){
  271. if($this->hook('output-warning',array($msg,$shift)))return true;
  272. echo "warning: $msg\n";
  273. }
  274. /** @obsolete */
  275. function outputDebug($msg,$shift=0){
  276. if($this->hook('output-debug',array($msg,$shift)))return true;
  277. echo "debug: $msg\n";
  278. }
  279. // }}}
  280. // {{{ Configuration File Handling
  281. /** Executed when trying to access config parameter which is not find in the file */
  282. function configExceptionOrDefault($default,$exceptiontext){
  283. if($default!='_config_get_false')return $default;
  284. throw new BaseException($exceptiontext);
  285. }
  286. /** Read config file and store it in $this->config. Use getConfig() to access */
  287. function readConfig($file='config.php'){
  288. $orig_file = $file;
  289. if(is_null($this->config))$this->config=array();
  290. $config=array();
  291. if(strpos($file,'/')===false){
  292. $file=getcwd().'/'.$file;
  293. }
  294. if (!file_exists($file)){
  295. foreach (explode(PATH_SEPARATOR, get_include_path()) as $path){
  296. $fullpath = $path . DIRECTORY_SEPARATOR . $orig_file;
  297. if (file_exists($fullpath)){
  298. $file = $fullpath;
  299. break;
  300. }
  301. }
  302. }
  303. if (file_exists($file)) {
  304. // some tricky thing to make config be read in some cases it could not in simple way
  305. if(!$config)global $config;
  306. include_once $file;
  307. }
  308. $this->config = array_merge($this->config,$config);
  309. $tz = $this->getConfig('timezone',null);
  310. if(!is_null($tz) && function_exists('date_default_timezone_set')){
  311. // with seting default timezone
  312. date_default_timezone_set($tz);
  313. }
  314. }
  315. /** Manually set configuration option */
  316. function setConfig($config=array()){
  317. if(!$config)$config=array();
  318. if(!$this->config)$this->config=array();
  319. $this->config=array_merge($this->config,$config);
  320. }
  321. /** Load config if necessary and look up corresponding setting */
  322. function getConfig($path, $default_value = undefined){
  323. /**
  324. * For given path such as 'dsn' or 'logger/log_dir' returns
  325. * corresponding config value. Throws ExceptionNotConfigured if not set.
  326. *
  327. * To find out if config is set, do this:
  328. *
  329. * $var_is_set=true;
  330. * try { $api->getConfig($path); } catch ExceptionNotConfigured($e) { $var_is_set=false; };
  331. */
  332. if(is_null($this->config)){
  333. $this->readConfig('config-default.php');
  334. $this->readConfig();
  335. }
  336. $parts = explode('/',$path);
  337. $current_position = $this->config;
  338. foreach($parts as $part){
  339. if(!array_key_exists($part,$current_position)){
  340. if($default_value!==undefined)return $default_value;
  341. throw $this->exception("Configuration parameter is missing in config.php",'NotConfigured')
  342. ->addMoreInfo("missign_line"," \$config['".join("']['",explode('/',$path))."']");
  343. }else{
  344. $current_position = $current_position[$part];
  345. }
  346. }
  347. return $current_position;
  348. }
  349. // }}}
  350. // {{{ Version handling
  351. /** Determine version of Agile Toolkit or specified plug-in */
  352. private $version_cache=null;
  353. function getVersion($of='atk'){
  354. // TODO: get version of add-on
  355. if(!$this->version_cache){
  356. $f=$this->api->pathfinder->atk_location->base_path.DIRECTORY_SEPARATOR.'VERSION';
  357. if(file_exists($f)){
  358. $this->version_cache=trim(file_get_contents($f));
  359. }else{
  360. $this->version_cache='4.0.1';
  361. }
  362. }
  363. return $this->version_cache;
  364. }
  365. /** Verifies version. Should be used by addons. For speed improvement, redefine this into empty function */
  366. function requires($addon='atk',$v,$return_only=false){
  367. $cv=$this->getVersion($addon);
  368. if(version_compare($cv,$v)<0){
  369. if($addon=='atk'){
  370. $e=$this->exception('Agile Toolkit version is too old');
  371. }else{
  372. $e=$this->exception('Add-on is outdated')
  373. ->addMoreInfo('addon',$addon);
  374. }
  375. $e->addMoreInfo('required',$v)
  376. ->addMoreInfo('you have',$cv);
  377. throw $e;
  378. }
  379. // Possibly we need to enable compatibility version
  380. if($addon=='atk'){
  381. if(
  382. version_compare($v,'4.2')<0 &&
  383. version_compare($v,'4.1.4')>=0
  384. ){
  385. $this->add('Controller_Compat');
  386. return true;
  387. }
  388. }
  389. return true;
  390. }
  391. /** @obsolete use @requires */
  392. function versionRequirement($v,$return_only=false){
  393. return $this->requires('atk',$v,$return_only);
  394. }
  395. // }}}
  396. // {{{ Database connection handling
  397. /** Use database configuration settings from config file to establish default connection */
  398. function dbConnect($dsn=null){
  399. $this->db=$this->add('DB')->connect($dsn);
  400. return $this;
  401. }
  402. /** Attempts to connect, but does not raise exception on failure. */
  403. function tryConnect($dsn){
  404. $this->db=DBlite::tryConnect($dsn);
  405. }
  406. // }}}
  407. // {{{ Helper / utility methods
  408. /**
  409. * Normalize field or identifier name. Can also be used in URL normalization.
  410. * This will replace all non alpha-numeric characters with separator.
  411. * Multiple separators in a row is replaced with one.
  412. * Separators in beginning and at the end of name are removed.
  413. *
  414. * @param string $name String to process
  415. * @param string $separator Character acting as separator
  416. *
  417. * @return string Normalized string
  418. */
  419. function normalizeName($name,$separator='_')
  420. {
  421. if(strlen($separator)==0) {
  422. return preg_replace('|[^a-z0-9]|i','',$name);
  423. }
  424. $s = $separator[0];
  425. $name = preg_replace('|[^a-z0-9\\'.$s.']|i',$s,$name);
  426. $name = trim($name,$s);
  427. $name = preg_replace('|\\'.$s.'{2,}|',$s,$name);
  428. return $name;
  429. }
  430. /**
  431. * Normalize class name.
  432. * This will add specified prefix to class name if it's not already added.
  433. * Class name can have namespaces and they are treated prefectly.
  434. *
  435. * @param string|object $name Name of class or object
  436. * @param string $prefix Prefix for class name
  437. *
  438. * @return string|object Full class name or received object
  439. */
  440. function normalizeClassName($name,$prefix)
  441. {
  442. if(!is_string($name)) return $name;
  443. $name = str_replace('/','\\',$name);
  444. if($prefix) {
  445. $class = ltrim(strrchr($name,'\\'),'\\')?:$name;
  446. $prefix = ucfirst($prefix);
  447. if (strpos($class,$prefix)!==0) {
  448. $name = preg_replace('|^(.*\\\)?(.*)$|', '\1'.$prefix.'_\2', $name);
  449. }
  450. }
  451. return $name;
  452. }
  453. // }}}
  454. }