1 : <?php
2 :
3 : /**
4 : * PHPDevShell is a RAD Framework aimed at developing administrative applications.
5 : *
6 : * @package PHPDevShell
7 : * @link http://www.phpdevshell.org
8 : * @copyright Copyright (C) 2007 Jason Schoeman, All rights reserved.
9 : * @license GNU/LGPL, see readme/licensed_under_lgpl or http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
10 : * @author Jason Schoeman, Contact: titan [at] phpdevshell [dot] org.
11 : *
12 : * Copyright notice: See readme/notice
13 : * By using PHPDevShell you agree to notice and license, if you dont agree to this notice/license you are not allowed to use PHPDevShell.
14 : *
15 : * This is the main engine.
16 : */
17 1 : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
18 : // Engine for PHPDevShell, this is what makes the system run, it calls all needed classes on each request. //
19 : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
20 :
21 : define('phpdevshell_version', 'PHPDevShell V 3.0.5-Stable-DB-3004');
22 : define('phpdevshell_db_version', '3004');
23 :
24 :
25 : require_once 'PHPDS_utils.inc.php';
26 :
27 : /**
28 : * PHPDS: the main class for the PHPDevShell engine
29 : *
30 : * It can be used either standalone or embedded
31 : *
32 : * NOTE: none of these methods are meant to be called by you. For standalone mode, consult PHPDevShell programmers' guide. For embedded mode, use the PHPDSlib
33 : *
34 : * @date 20090427
35 : * @version 1.0
36 : *
37 : */
38 20 : class PHPDS
39 : {
40 : /**
41 : * Core system configuration settings.
42 : *
43 : * @var object
44 : */
45 : protected $configuration;
46 : /**
47 : * Core system language values.
48 1 : *
49 : * @var object
50 : */
51 : protected $lang;
52 : /**
53 : * Core database object.
54 : *
55 : * @var object
56 : */
57 : protected $db;
58 : /**
59 : * Core object.
60 1 : *
61 : * @var object
62 : */
63 : protected $core;
64 : /**
65 : * Core user object.
66 : *
67 : * @var object
68 : */
69 : protected $user;
70 : /**
71 : * Core security object.
72 : *
73 : * @var object
74 : */
75 : protected $security;
76 : /**
77 1 : * Core navigation object.
78 : *
79 : * @var object
80 1 : */
81 : protected $navigation;
82 : /**
83 : * Core template object.
84 : *
85 : * @var object
86 : */
87 : protected $template;
88 : /**
89 : * Main instance of the debug module, which handles startup init.
90 : *
91 : * @var object
92 : */
93 : protected $debugInstance;
94 : /**
95 : * Main instance of the tagger module
96 : */
97 : protected $tagger;
98 : /**
99 20 : * Main instance of the notification module
100 : *
101 : * @var PHPDS_notif
102 20 : */
103 : protected $notif;
104 : /**
105 : * PHPDS is used throught the lib (true) or standalone (false).
106 : *
107 : * @var boolean
108 : */
109 : protected $embedded;
110 : /**
111 : * The textual path of PHPDS directory on disk (not the URL).
112 : *
113 : * @var string
114 : */
115 : protected $basepath;
116 : /**
117 : * The higher the less backward-compatible.
118 : *
119 : * @var int
120 : */
121 : protected $compatMode = 1;
122 : /**
123 : * Contains array of installed supportive plugin classes.
124 : *
125 : * @var array
126 : */
127 20 : public $PluginClasses;
128 : /**
129 : * Execution stage (i.e. run level)
130 : *
131 : * @var int
132 : */
133 : protected $stage = 1; // 1 => initialization ; 2 => running
134 : /**
135 : * Constructor: initialize the instance and starts the configuration process
136 : *
137 : * @param $embedded
138 : * @date 20100618 (v1.0.2) (greg) added exception handler to display config parameters to allow inspection in a problematic installation
139 : * @date 20091006 Improved windoz compat by using DIRECTORY_SEPARATOR
140 : * @author greg
141 : * @version 1.0.2
142 : * @return itself
143 : */
144 : public function __construct($embedded = false)
145 3 : {
146 : // Start Ob.
147 1 : ob_start();
148 1 : $this->embedded = $embedded;
149 1 : $this->basepath = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..') . DIRECTORY_SEPARATOR;
150 : try {
151 1 : $this->config();
152 1 : } catch (Exception $e) {
153 0 : $this->PHPDS_errorHandler()->doHandleException($e);
154 : }
155 1 : return $this;
156 : }
157 :
158 : /**
159 : * Destruct Class.
160 : *
161 : * @date 20091030 mute possible error with @ (in case destruct is called before ob is started)
162 : * @author greg
163 : * @version 1.0.1
164 : * @return nothing
165 2 : *
166 : */
167 : public function __destruct()
168 : {
169 : // Flush.
170 0 : PU_cleanBuffers(true);
171 0 : }
172 :
173 : /**
174 : * Load one config file, if it exists
175 : *
176 : * If it's ok, its name is added to $configuration['config_files']
177 : *
178 : * @param $path absolute file name
179 : * @return boolean wether it's successfull
180 : * @date 20090521 Added
181 : * @date 20091006 Changed to dual list of files (used/missing)
182 : * @date 20100630 (v1.1.1) (greg) fixed the misplaced _log() call
183 : * @date 20110225 (v1.1.2) (greg) fixed _log() to log() ; remove pre/postfix on $path
184 : * @author greg <greg@phpdevshell.org>
185 : * @version 1.1.2
186 : */
187 : protected function includeConfigFile($path, &$configuration)
188 : {
189 0 : if (empty($path)) return false;
190 :
191 0 : if (!file_exists($path)) {
192 0 : $configuration['config_files_missing'][] = $path;
193 0 : return false;
194 : }
195 0 : $this->log("Loading config file $path");
196 0 : include_once $path;
197 0 : $configuration['config_files_used'][] = $path;
198 0 : return true;
199 : }
200 :
201 : /**
202 : * Load one config file, if it exists; also tried a ".local" with the same name
203 : *
204 : * @param $filename short file name
205 : * @return nothing
206 : * @date 20090521 Added
207 : * @date 20091006 Changed to dual list of files (used/missing)
208 : * @version 1.0
209 : */
210 : protected function loadConfigFile($filename, &$configuration)
211 : {
212 0 : if (empty($filename)) return false;
213 :
214 0 : $this->log("Looking for general config file \"$filename\"");
215 :
216 0 : $this->includeConfigFile($this->basepath('config') . $filename . '.config.php', $configuration);
217 0 : }
218 :
219 : /**
220 : * Load plugin-specific host-style config
221 : *
222 : * This allows plugins to provide a configuration, for example when 1 plugin <=> 1 site
223 : *
224 : */
225 : protected function loadPluginsConfig(&$configuration)
226 20 : {
227 20 : $files = glob($this->basepath('plugins').'/*/config/host.config.php');
228 0 : if (! empty($files)) {
229 0 : foreach($files as $filename) {
230 0 : $this->log("Looking for plugin config file \"$filename\"");
231 0 : $this->includeConfigFile($filename, $configuration);
232 0 : }
233 0 : }
234 0 : }
235 :
236 : /**
237 : * Create a config from the config files, and store it in the instance field
238 : *
239 : * The actual configuration is loaded from two files (one is generic, the other is specific)
240 : * from the config/ folder.
241 : *
242 : * @date 20090427
243 : * @date 20090521 Make it lighter by using loadConfigFile()
244 : * @date 20091006 Changed to dual list of files (used/missing)
245 : * @date 20110225 (v1.1.1) (greg) added support for plugin-specific host-style config files
246 : * @version 1.1.1
247 : *
248 : * @return PHPDS the current instance
249 : */
250 : protected function loadConfig()
251 : {
252 : // starts with an empty array which will contain the actual names of the configurations files used
253 0 : $configuration = array();
254 0 : $configuration['config_files_used'] = array();
255 0 : $configuration['config_files_missing'] = array();
256 :
257 0 : $configuration['phpdevshell_version'] = phpdevshell_version;
258 0 : $configuration['phpdevshell_db_version'] = phpdevshell_db_version;
259 :
260 0 : if (!is_dir($this->basepath('config'))) {
261 0 : print 'The folder "<tt>' . $this->basepath . 'config</tt>" is missing.<br />';
262 0 : print 'It must be present and contain at least one config file.<br />';
263 0 : print 'Read the install instruction on creating a config file and installing the database.<br>';
264 1 : print '<a href="http://wiki.phpdevshell.org/wiki/Installing_PHPDevShell" target="_blank">Click here to read install instructions.</a><br>';
265 0 : print 'PHPDevShell basepath is "<tt>' . $this->basepath . '</tt>".<br>';
266 0 : exit();
267 : }
268 :
269 0 : $this->loadConfigFile('PHPDS-defaults', $configuration);
270 0 : $this->loadConfigFile('multi-host', $configuration);
271 0 : $this->loadConfigFile('single-site', $configuration);
272 :
273 0 : $this->loadPluginsConfig($configuration);
274 :
275 0 : if (!empty($_SERVER['SERVER_NAME'])) {
276 0 : if (!empty($configuration['host'][$_SERVER['SERVER_NAME']])) {
277 0 : $this->loadConfigFile($configuration['host'][$_SERVER['SERVER_NAME']], $configuration);
278 0 : }
279 0 : $this->loadConfigFile($_SERVER['SERVER_NAME'], $configuration);
280 0 : }
281 :
282 0 : if (count($configuration['config_files_used']) == 0) {
283 0 : print 'No config file found for host "' . $_SERVER['SERVER_NAME'] . '" inside folder "<tt>' . $this->basepath('config') . '</tt>"<br>';
284 0 : print 'At least one file among those listed below should be readable.<br />';
285 0 : print 'Read the install instruction on creating a config file and installing the database.<br>';
286 0 : print '<a href="http://wiki.phpdevshell.org/wiki/Installing_PHPDevShell" target="_blank">Click here to read install instructions.</a><br>';
287 0 : print 'PHPDevShell basepath is "<tt>' . $this->basepath . '</tt>".<br>';
288 0 : print 'All these files were tried:<br><tt>';
289 0 : print implode('<br>', $configuration['config_files_missing']);
290 0 : print '</tt>';
291 0 : exit();
292 : }
293 0 : $this->PHPDS_configuration($configuration);
294 0 : $success = spl_autoload_register(array($this, "PHPDS_autoloader"));
295 0 : return $this; // to allow fluent interface
296 : }
297 :
298 : /**
299 : * Deal with the "session" part of the configuration
300 : *
301 : * In standalone mode, create the sessions ; in embedded mode,
302 : * fetch the current session (we hijack the session, we don't create a new one)
303 : *
304 : * NOTE: in embedded mode you MUST create a session before using PHPDSlib
305 : *
306 : * @date 20090427
307 : * @version 1.0
308 : *
309 : * @return PHPDS the current instance
310 : */
311 : protected function configSession()
312 : {
313 0 : if (empty($this->configuration['absolute_url'])) {
314 0 : $protocol = empty($_SERVER['HTTPS']) ? 'http://' : 'https://';
315 0 : $this->configuration['absolute_url'] = $protocol . $_SERVER['HTTP_HOST'] . str_replace('/index.php', '', $_SERVER['PHP_SELF']);
316 0 : }
317 :
318 0 : if ($this->embedded) {
319 0 : $this->configuration['session_name'] = session_name();
320 : // TODO: deal with empty session
321 0 : } else {
322 0 : if (!empty($this->configuration['absolute_url']))
323 0 : $this->configuration['session_name'] = md5($this->configuration['absolute_url']);
324 :
325 0 : if (!empty($this->configuration['session_name']))
326 0 : session_name(md5($this->configuration['session_name']));
327 0 : if (!empty($this->configuration['session_path']))
328 0 : session_save_path($this->configuration['session_path']);
329 :
330 : try {
331 0 : $p = realpath(session_save_path());
332 0 : session_start();
333 0 : } catch (Exception $e) {
334 0 : throw new Exception('Unable to start the session. This is probably because the session folder is not writable or missing.<br />Please check that the folder "' . session_save_path() . '" is present and writable, then reload this page.');
335 : }
336 : }
337 :
338 : // Make sure we dont keep our session longer then it should be.
339 0 : if (! empty($this->configuration['session_life'])) {
340 0 : if (isset($_SESSION['SYSTEM_SESSION_TIME']) && (time() - $_SESSION['SYSTEM_SESSION_TIME']) > $this->configuration['session_life']) {
341 : // Session can now be destroyed.
342 0 : session_destroy();
343 0 : session_regenerate_id(true);
344 0 : $_SESSION = array();
345 0 : } else {
346 0 : $_SESSION['SYSTEM_SESSION_TIME'] = time();
347 : }
348 0 : }
349 :
350 0 : return $this; // to allow fluent interface
351 : }
352 :
353 : /**
354 : * Deal with database access configuration
355 : *
356 : * @date 20090427
357 : * @version 1.0
358 : *
359 : * @return PHPDS the current instance
360 : */
361 : protected function configDb()
362 : {
363 0 : $db = $this->PHPDS_db();
364 0 : $configuration = $this->configuration;
365 0 : $db->server = $configuration['server_address'];
366 0 : $db->dbUsername = $configuration['database_user_name'];
367 0 : $db->dbName = $configuration['database_name'];
368 0 : $db->dbPassword = $configuration['database_password'];
369 0 : $db->connect();
370 0 : $db->connectCacheServer();
371 :
372 0 : return $this; // to allow fluent interface
373 : }
374 :
375 : /**
376 : * Copy settings from the database-loaded array. Converts and defaults to false if the value isn't set
377 : *
378 : * @date 20100219
379 : * @author greg
380 : * @version 1.1
381 : * @param $source array, the array to extract values from
382 : * @param $target array, the array to add the values to
383 : * @param $indexes array, the indexes of the values to copy
384 : * @param $type string, the type of value to cast (currently only boolean or null for everything else)
385 : * @return nothing
386 : */
387 : protected function copySettings($settings, $type = null)
388 : {
389 0 : $this->copyArray($this->PHPDS_db()->essentialSettings, $this->configuration, $settings, $type);
390 0 : }
391 :
392 : /**
393 : * Copy an array to another and defaults to false if the value isn't set
394 : *
395 : * @date 20100219
396 : * @author greg
397 : * @version 1.0
398 : * @param $source array, the array to extract values from
399 : * @param $target array, the array to add the values to
400 : * @param $indexes array, the indexes of the values to copy
401 : * @param $type string, the type of value to cast (currently only boolean or null for everything else)
402 : * @return nothing
403 : */
404 : public function copyArray($source, &$target, $indexes, $type = null)
405 : {
406 0 : if (!is_array($indexes)) $indexes = array($indexes);
407 : switch ($type) {
408 0 : case 'boolean':
409 0 : foreach ($indexes as $index) {
410 0 : $target[$index] = isset($source[$index]) ? (boolean) $source[$index] : false;
411 0 : }
412 0 : break;
413 0 : default:
414 0 : foreach ($indexes as $index) {
415 0 : $target[$index] = isset($source[$index]) ? $source[$index] : false;
416 0 : }
417 0 : }
418 0 : }
419 :
420 : /**
421 : * Fetch core settings from the site configuration stored in the database
422 : * Also fetch some settings from the session and the locales
423 : *
424 : * @date 20090513
425 : * @version 1.0.1
426 : *
427 : * @return PHPDS the current instance
428 : */
429 : protected function configCoreSettings()
430 : {
431 : // Set core settings. //////////////////////////////////////////////////////////////
432 0 : $this->configuration['absolute_path'] = $this->basepath(); /////////////////////////
433 0 : $this->copySettings($this->configuration['preloaded_settings']); ///////////////////
434 : ////////////////////////////////////////////////////////////////////////////////////
435 :
436 : // Prepares login. /////////////////////////////////////////////////////////////////
437 0 : $this->PHPDS_user()->controlLogin(); ///////////////////////////////////////////////
438 :
439 : // Asign locale to use. ////////////////////////////////////////////////////////////
440 0 : $this->configuration['locale'] = $this->configuration['user_locale']; //////////////
441 :
442 : // Set environment. ////////////////////////////////////////////////////////////////
443 0 : putenv('LANG=' . $this->configuration['locale']); //////////////////////////////////
444 :
445 : // Set locale. /////////////////////////////////////////////////////////////////////
446 0 : setlocale(LC_ALL, $this->configuration['locale']); /////////////////////////////////
447 :
448 : // Server date and timezones. //////////////////////////////////////////////////////
449 0 : date_default_timezone_set($this->configuration['system_timezone']); ////////////////
450 :
451 : // Assign clean locale directory. //////////////////////////////////////////////////
452 0 : $this->configuration['locale_dir'] = $this->PHPDS_core()->formatLocale(false); /////
453 :
454 : ////////////////////////////////////////////////////////////////////////////////////
455 0 : return $this; // to allow fluent interface
456 : }
457 :
458 : /**
459 : * Loads extra broad based system functions.
460 : * @return void
461 : */
462 : public function loadFunctions()
463 : {
464 0 : if (!empty($this->configuration['function_files'])) {
465 0 : foreach ($this->configuration['function_files'] as $function_file) {
466 0 : $function_file = $this->basepath('includes') . $function_file;
467 0 : if (file_exists($function_file)) {
468 0 : include_once ($function_file);
469 0 : }
470 0 : }
471 0 : }
472 0 : }
473 :
474 : /**
475 : * Main configuration method: load settings, create menus, templates, and so on
476 : * After that, everything is ready to run
477 : * all config_*() methods are meant to be called only at startup time by config()
478 : *
479 : * @version 1.0.2
480 : *
481 : * @date 20100630 (v1.0.1) (greg) moved loading of user functions up TODO: maybe we should hardcode the ones we're using??
482 : * @date 20100810 (v1.0.2) (greg) rearranged calls to allow switching to install.php if needed
483 : *
484 : * @return PHPDS the current instance
485 : */
486 : protected function config()
487 : {
488 : ///////////////////////////////////////////////////////////////////////
489 : // load various configuration files. //////////////////////////////////
490 0 : $this->loadConfig(); //////////////////////////////////////////////////
491 :
492 : // Does the user want utils to load. //////////////////////////////////
493 0 : $this->loadFunctions(); ///////////////////////////////////////////////
494 :
495 : // Init error engine. /////////////////////////////////////////////////
496 0 : $this->PHPDS_errorHandler(); //////////////////////////////////////////
497 :
498 : // Init main debug instance. //////////////////////////////////////////
499 0 : $this->PHPDS_debug(); /////////////////////////////////////////////////
500 :
501 : // Various init subroutines. //////////////////////////////////////////
502 0 : $this->configSession()->configDb(); ///////////////////////////////////
503 :
504 : // Loads important settings from db enabling system to run correctly. /
505 0 : $this->db->getEssentialSettings(); ////////////////////////////////////
506 :
507 : // Loads available plugins from database. /////////////////////////////
508 0 : $this->PluginClasses = $this->db->LoadPluginClasses(); ////////////////
509 :
510 : // Loads settings from configuration file. ////////////////////////////
511 0 : $this->configCoreSettings(); //////////////////////////////////////////
512 :
513 : // Checks which plugins is installed. ////////////////////////////////
514 0 : $this->db->installedPlugins(); ////////////////////////////////////////
515 :
516 : // Loads menu language translation engine. ////////////////////////////
517 0 : $this->PHPDS_core()->loadMenuLanguage(); //////////////////////////////
518 :
519 : // This is both for templates and security: it builds the list of /////
520 : // resources the current user is allowed to ///////////////////////////
521 : // Parse the request string and set the menu item. ////////////////////
522 0 : $this->PHPDS_navigation()->extractMenu()->parseRequestString(); ///////
523 :
524 : // Template Folder ////////////////////////////////////////////////////
525 0 : $this->configuration['template_folder'] = $this->PHPDS_template()->getTemplate();
526 :
527 : // Set core language files. ///////////////////////////////////////////
528 0 : $this->PHPDS_core()->loadCoreLanguage(); //////////////////////////////
529 :
530 : // Set user session discription settings //////////////////////////////
531 0 : $this->configuration['user_display_name'] = $_SESSION['user_display_name'];
532 0 : $this->configuration['user_role_name'] = $_SESSION['user_role_name'];
533 0 : $this->configuration['user_group_name'] = $_SESSION['user_group_name'];
534 :
535 : // Set default plugin language files //////////////////////////////////
536 0 : $this->PHPDS_core()->loadDefaultPluginLanguage(); /////////////////////
537 : ///////////////////////////////////////////////////////////////////////
538 0 : }
539 :
540 : /**
541 : * Actual starting point of the (non-embedded) PHPDS engine
542 : *
543 : * @date 20090427
544 : * @version 1.0
545 : *
546 : * @return nothing
547 : */
548 : public function run()
549 : {
550 0 : $this->stage = 2;
551 : try {
552 : //TODO: check we don't run an embebbed instance
553 : // We require templating system for final output
554 : // Run template as required.
555 0 : $this->PHPDS_template()->runTemplate();
556 : // Lets log access to menu item if so required.
557 0 : $this->PHPDS_db()->logMenuAccess();
558 : // Write collected logs to database.
559 0 : $this->PHPDS_db()->logThis();
560 0 : } catch (Exception $e) {
561 0 : $this->PHPDS_errorHandler()->doHandleException($e);
562 : }
563 0 : }
564 : // accessors
565 :
566 : /**
567 : * Create a new instance of the given class and link it as dependant (arguments as an array)
568 : *
569 : * @param PHPDS|PHPDS_dependant $dependancy
570 : * @param string $classname name of the class to instanciate
571 : * @param array $params parameters to feed the object's constructor
572 : *
573 : * @date 20100217
574 : * @date 20100426 (v1.0.1) (greg) direct owner is now sent instead of the top owner
575 : * @date 20100922 (v1.1) (greg) moved from PHPDS_dependant to PHDS
576 : *
577 : * @version 1.1
578 : * @author greg
579 : *
580 : * @return instance of $classname
581 : */
582 : public function _factory($classname, $params = null, $dependancy = null)
583 : {
584 67 : if (!is_array($params)) $params = array();
585 67 : if (empty($dependancy)) $dependancy = $this;
586 :
587 : try {
588 67 : if (class_exists($classname, true)) {
589 67 : array_push($params, $this);
590 67 : $string = '';
591 67 : $count = count($params);
592 67 : for ($loop = 0; $loop < $count; $loop++) {
593 67 : if ($string) $string .= ', ';
594 67 : $string .= '$params[' . $loop . ']';
595 67 : }
596 67 : $c = "\$o = new $classname($string);";
597 67 : eval($c);
598 67 : if ($o instanceof $classname) {
599 67 : $o->PHPDS_dependance($dependancy);
600 67 : return $o;
601 : }
602 0 : throw new PHPDS_exception("Unable to factor a new \"$classname\".");
603 : } else {
604 0 : throw new PHPDS_exception("Can't find definition for class \"$classname\".");
605 : }
606 0 : } catch (Exception $e) {
607 0 : throw new PHPDS_exception("Error while factoring a new \"$classname\".", 0, $e);
608 : }
609 : }
610 : /*
611 : * Most fields contains objects which are "lazily created" (that is created
612 : * the first time they are asked for).
613 : */
614 :
615 : /**
616 : * Allow access to configuration, either read (no param) or write
617 : *
618 : * This makes possible to start with a forced configuration, for testing for example
619 : *
620 : * It returns the configuration array
621 : *
622 : * CAUTION: an array is not an object so be carefull to use & if you need to modify it
623 : *
624 : * @date 20090427
625 : * @version 1.0
626 : *
627 : * @param $configuration possibly a new configuration array
628 : * @return array the configuration array
629 : */
630 : public function PHPDS_configuration($configuration = null)
631 : {
632 24 : if (empty($this->configuration) && is_array($configuration)) {
633 0 : $this->configuration = new PHPDS_array($configuration);
634 0 : $this->configuration['time'] = time();
635 0 : if ($this->compatMode < 2)
636 0 : $GLOBALS['configuration'] = & $this->configuration;
637 0 : }
638 24 : return $this->configuration;
639 : }
640 :
641 : /**
642 : * Custom Error Handler.
643 : *
644 : * One is created if necessary.
645 : *
646 : * You can override to use you own core subsystem
647 : *
648 : * @date 20090618
649 : * @version 1.0
650 : *
651 : * @return error
652 : */
653 : public function PHPDS_errorHandler()
654 : {
655 1 : if (empty($this->errorHandler)) {
656 1 : $this->errorHandler = $this->_factory('PHPDS_errorHandler');
657 1 : if ($this->compatMode < 2) $GLOBALS['errorHandler'] = & $this->errorHandler;
658 1 : }
659 1 : return $this->errorHandler;
660 : }
661 :
662 : /**
663 : * Allow access to the (formerly) global core subsystem
664 : *
665 : * One is created if necessary.
666 : *
667 : * You can override to use you own core subsystem
668 : *
669 : * @date 20090427
670 : * @version 1.0
671 : *
672 : * @return core
673 : */
674 : public function PHPDS_core()
675 : {
676 0 : if (empty($this->core)) {
677 0 : $this->core = $this->_factory('PHPDS_core');
678 0 : if ($this->compatMode < 2) $GLOBALS['core'] = & $this->core;
679 0 : }
680 0 : return $this->core;
681 : }
682 :
683 : /**
684 : * Provides a variaty of user functions.
685 : *
686 : * One is created if necessary.
687 : *
688 : * You can override to use you own core subsystem
689 : *
690 : * @date 20101111
691 : * @version 1.0
692 : *
693 : * @return user
694 : */
695 : public function PHPDS_user()
696 : {
697 0 : if (empty($this->user)) {
698 0 : $this->user = $this->_factory('PHPDS_user');
699 0 : if ($this->compatMode < 2) $GLOBALS['user'] = & $this->user;
700 0 : }
701 0 : return $this->user;
702 : }
703 :
704 : /**
705 : * Allow access to the global debugging subsystem
706 : *
707 : * One is created if necessary.
708 : *
709 : * You can override to use you own core subsystem
710 : *
711 : * @date 20100412 (v1.0.1) default domain is now "skel"
712 : * @date 20090615 (v1.0) creation
713 : * @version 1.0.1
714 : *
715 : *
716 : * @return debug instance
717 : */
718 : public function PHPDS_debug()
719 : {
720 0 : if (empty($this->debug)) {
721 0 : $domain = ($this->embedded ? 'authlib' : 'skel');
722 0 : $this->debug = $this->_factory('PHPDS_debug', $domain);
723 0 : }
724 0 : return $this->debug;
725 : }
726 :
727 : /**
728 : * Send info data to the debug subsystem (console, firebug, ...)
729 : *
730 : * The goal of this function is to be called all thourough the code to be able to track bugs.
731 : *
732 : * @param string $data
733 : */
734 : public function log($data)
735 : {
736 3 : if (!empty($this->debug)) $this->PHPDS_debug()->debug($data);
737 : else {
738 3 : if (!empty($GLOBALS['early_debug'])) error_log('EARLY INFO: ' . $data);
739 : }
740 3 : }
741 :
742 : /**
743 : * Alias for log() - don't use it doesn't follow the guidelines
744 : * @deprecated
745 : * @param unknown_type $data
746 : */
747 : public function _log($data)
748 : {
749 3 : return $this->log($data);
750 : }
751 :
752 : /**
753 : * Allow access to the (formerly) global navigation subsystem
754 : *
755 : * One is created if necessary.
756 : *
757 : * You can override to use you own navigation subsystem
758 : *
759 : * @date 20090427
760 : * @version 1.0
761 : *
762 : * @return navigation
763 : */
764 : public function PHPDS_navigation()
765 : {
766 20 : if (empty($this->navigation)) {
767 1 : $this->navigation = $this->_factory('PHPDS_navigation');
768 1 : if ($this->compatMode < 2) $GLOBALS['navigation'] = & $this->navigation;
769 1 : }
770 20 : return $this->navigation;
771 : }
772 :
773 : /**
774 : * Allow access to the (formerly) global database subsystem
775 : *
776 : * One is created if necessary.
777 : *
778 : * You can override to use you own database subsystem
779 : *
780 : * @date 20090427
781 : * @version 1.0
782 : *
783 : * @return db
784 : */
785 : public function PHPDS_db()
786 : {
787 20 : if (empty($this->db)) {
788 1 : $this->db = $this->_factory('PHPDS_db');
789 1 : if ($this->compatMode < 2) $GLOBALS['db'] = & $this->db;
790 1 : }
791 20 : return $this->db;
792 : }
793 :
794 : /**
795 : * Allow access to the (formerly) global security subsystem
796 : *
797 : * One is created if necessary.
798 : *
799 : * You can override to use you own security subsystem
800 : *
801 : * @date 20090427
802 : * @version 1.0
803 : *
804 : * @return security
805 : */
806 : public function PHPDS_security()
807 : {
808 0 : if (empty($this->security)) {
809 0 : $this->security = $this->_factory('PHPDS_security');
810 0 : if ($this->compatMode < 2) $GLOBALS['security'] = & $this->security;
811 0 : }
812 0 : return $this->security;
813 : }
814 :
815 : /**
816 : * Allow access to the (formerly) global templating subsystem
817 : *
818 : * One is created if necessary.
819 : *
820 : * You can override to use you own templating subsystem
821 : *
822 : * @date 20100928 (v1.1) (greg) added $lazy param
823 : * @date 20090427 (v1.0) (greg) created
824 : * @version 1.1
825 : * @param boolean $lazy if true (default) the template is created if wasn't before
826 : *
827 : * @return template
828 : */
829 : public function PHPDS_template($lazy = true)
830 : {
831 23 : if (empty($this->template) && $lazy) {
832 1 : $this->template = $this->_factory('PHPDS_template');
833 1 : if ($this->compatMode < 2) $GLOBALS['template'] = & $this->template;
834 1 : }
835 23 : return $this->template;
836 : }
837 :
838 : /**
839 : * Allow access to the tagging subsystem
840 : *
841 : * One is created if necessary.
842 : *
843 : * You can override to use you own tagging subsystem
844 : *
845 : * @date 20100825
846 : * @version 1.0
847 : * @author greg
848 : *
849 : * @return PHPDS_tagger
850 : */
851 : public function PHPDS_tagger()
852 : {
853 0 : if (empty($this->tagger)) {
854 0 : $this->tagger = $this->_factory(('PHPDS_tagger'));
855 0 : }
856 0 : return $this->tagger;
857 : }
858 :
859 : /**
860 : * Allow access to the aynschronous notifications subsystem
861 : *
862 : * One is created if necessary.
863 : *
864 : * You can override to use you own tagging subsystem
865 : *
866 : * @date 20110615
867 : * @version 1.0
868 : * @author greg
869 : *
870 : * @return PHPDS_notif
871 : */
872 : public function PHPDS_notif()
873 : {
874 0 : if (empty($this->notif)) {
875 0 : $this->notif = $this->_factory(('PHPDS_notif'));
876 0 : }
877 0 : return $this->notif;
878 : }
879 :
880 : /**
881 : * Allow access to the (formerly) global templating subsystem
882 : *
883 : * One is created if necessary.
884 : *
885 : * You can override to use you own templating subsystem
886 : *
887 : * @date 20090427
888 : * @version 1.0
889 : *
890 : * @return template
891 : */
892 : public function PHPDS_lang()
893 : {
894 0 : if (empty($this->lang)) {
895 0 : $this->lang = new PHPDS_array();
896 0 : if ($this->compatMode < 2) $GLOBALS['lang'] = & $this->lang;
897 0 : }
898 0 : return $this->lang;
899 : }
900 :
901 : /**
902 : * This is a generic accessor to allow field access through an homogeneous and controlled way.
903 : * For example:
904 : *
905 : * $instance->get('core')
906 : *
907 : * will return the core object as returned by the core() accessor method
908 : *
909 : * @date 20090427
910 : * @version 1.0
911 : *
912 : * @param $field
913 : * @return mixed depends on what is asked for
914 : */
915 : public function get($field)
916 : {
917 26 : $method_name = 'PHPDS_' . $field;
918 26 : if (method_exists($this, $method_name)) {
919 24 : return $this->$method_name();
920 : }
921 2 : throw new Exception('Class "' . get_class($this) . '" has no field "' . $field . '", sorry!');
922 : }
923 :
924 : /**
925 : * Check if instance is embedded.
926 : *
927 : * @return mixed
928 : */
929 : public function isEmbedded()
930 : {
931 1 : return !empty($this->embedded);
932 : }
933 :
934 : /**
935 : * Simply returns basepath
936 : *
937 : * An optional postfix (i.e. folder name) can be given to retrieve the path a subfolder
938 : *
939 : * @author greg
940 : * @date 2009106 fix a problem when the path is invalid
941 : * @version 1.0.1
942 : * @return string
943 : */
944 : public function basepath($postfix = '')
945 : {
946 3 : $path = realpath($this->basepath . $postfix);
947 3 : if ($path) return $path . DIRECTORY_SEPARATOR; else return false;
948 : }
949 :
950 : /**
951 : * Try to load a class from a file
952 : *
953 : * NOTE: for performance reason we DON'T try first to see if the class already exists
954 : *
955 : * @deprecated
956 : * @param $classname name of the class to look for
957 : * @param $filename name of the file to look into
958 : * @return boolean wether we found the class or not
959 : */
960 : public function sneak_class($classname, $filename)
961 : {
962 0 : $this->sneakClass($classname, $filename);
963 0 : }
964 :
965 : /**
966 : * Try to load a class from a file
967 : *
968 : * NOTE: for performance reason we DON'T try first to see if the class already exists
969 : *
970 : * @param $classname name of the class to look for
971 : * @param $filename name of the file to look into
972 : * @return boolean wether we found the class or not
973 : */
974 : public function sneakClass($classname, $filename)
975 : {
976 3 : if (is_file($filename)) {
977 3 : include_once ($filename);
978 3 : if (class_exists($classname, false)) {
979 3 : $this->_log("Autoloading $classname from $filename");
980 3 : return true;
981 : }
982 0 : }
983 3 : return false;
984 : }
985 :
986 : /**
987 : * Autoloader: when a class is instanciated, this method will load the proper php file
988 : *
989 : * Note: the various folders where the files are looked for depends on the
990 : * instance configuration, and on the current plugin
991 : *
992 : * A model file is also loaded if present
993 : *
994 : * @date 20100215 (greg) added manual overloading by configuration file ; enable cascading in case a file is found but the doesn't define the class
995 : * @version 1.2
996 : * @author jason
997 : * @param $class_name
998 : * @return irrelevant, it's a callback for the new() operator (however we return true on success, false otherwise)
999 : */
1000 : function PHPDS_autoloader($class_name)
1001 : {
1002 3 : $configuration = $this->PHPDS_configuration();
1003 3 : $absolute_path = $this->basepath();
1004 :
1005 : // Check if we have plugin classes to load.
1006 3 : if (!empty($this->PluginClasses[$class_name])) {
1007 0 : $engine_include_path = $absolute_path . 'plugins/' . $this->PluginClasses[$class_name]['plugin_folder'] . '/includes/' . $class_name . '.class.php';
1008 0 : $query_include_path = $absolute_path . 'plugins/' . $this->PluginClasses[$class_name]['plugin_folder'] . '/models/' . $class_name . '.query.php';
1009 0 : if ($this->sneakClass($class_name, $engine_include_path)) {
1010 0 : $this->sneakClass($class_name, $query_include_path);
1011 0 : return true;
1012 : }
1013 0 : }
1014 :
1015 : // Engine classes default directories
1016 3 : $includes = array('includes/local', 'includes', 'includes/legacy');
1017 3 : foreach ($includes as $path) {
1018 3 : $engine_include_path = $absolute_path . $path . '/' . $class_name . '.class.php';
1019 3 : if ($this->sneakClass($class_name, $engine_include_path)) {
1020 3 : $query_include_path = $absolute_path . $path . '/models/' . $class_name . '.query.php';
1021 3 : $this->sneakClass($class_name, $query_include_path);
1022 3 : return true;
1023 : }
1024 3 : $engine_include_path = $absolute_path . $path . 'default.class.php';
1025 3 : if ($this->sneakClass($class_name, $engine_include_path)) return true;
1026 3 : }
1027 :
1028 : // Try the plugin files - if a plugin is currently running
1029 0 : if (!empty($configuration['m'])) {
1030 0 : $navigation = $this->PHPDS_navigation();
1031 0 : if (!empty($navigation->navigation[$configuration['m']]['plugin'])) {
1032 :
1033 : // Check if file exists in active plugins folder.
1034 0 : $plugin_include_class = $absolute_path . 'plugins/' . $navigation->navigation[$configuration['m']]['plugin'] . '/includes/' . $class_name . '.class.php';
1035 0 : if ($this->sneakClass($class_name, $plugin_include_class)) {
1036 0 : $plugin_include_query = $absolute_path . 'plugins/' . $navigation->navigation[$configuration['m']]['plugin'] . '/models/' . $class_name . '.query.php';
1037 0 : $this->sneakClass($class_name, $plugin_include_query);
1038 0 : return true;
1039 : }
1040 :
1041 0 : $plugin_include_class = $absolute_path . 'plugins/' . $navigation->navigation[$configuration['m']]['plugin'] . '/includes/default.class.php';
1042 0 : if ($this->sneakClass($class_name, $plugin_include_class)) return true;
1043 0 : }
1044 0 : }
1045 :
1046 : // Check PHPDS's own include folder
1047 0 : $phpdev_include_class = $absolute_path . 'plugins/PHPDevShell/includes/' . $class_name . '.class.php';
1048 0 : if ($this->sneakClass($class_name, $phpdev_include_class)) return true;
1049 :
1050 : // Oh this is becoming a problem, perhaps we can locate the file here.
1051 0 : if (!empty($configuration['plugin_alt'])) {
1052 : // Ok last chance, is it in here?
1053 0 : $plugin_include_class = $absolute_path . $configuration['plugin_alt'] . 'includes/' . $class_name . '.class.php';
1054 0 : if ($this->sneakClass($class_name, $plugin_include_class)) return true;
1055 0 : }
1056 :
1057 0 : return false;
1058 : }
1059 : }
1060 :
1061 : /**
1062 : * This is a base class for PHPDS subsystems
1063 : *
1064 : * It allows dependency injection and dependency fetching; also mimics multiple inheritance
1065 : *
1066 : * @date 20090427
1067 : * @version 1.0.2 (greg)
1068 : * @author greg
1069 : *
1070 : */
1071 : class PHPDS_dependant
1072 : {
1073 : /**
1074 : * The object this object depend on. Ultimately up the chain it should be the main PHPSD instance
1075 : * @var PHPDS_dependant or PHPDS
1076 : */
1077 : protected $dependance;
1078 : /**
1079 : * The private debug instance of this object (with domain selection)
1080 : * @var debugInstance
1081 : */
1082 : private $debugInstance;
1083 : /**
1084 : * Holds a dependent extended object to appear as parent.
1085 : * @var object or name of the field containing the object
1086 : */
1087 : protected $parent;
1088 :
1089 : /**
1090 : * magic constructor
1091 : *
1092 : * parameter list is not explicit: we're expecting the LAST argument to be the dependence, other are fed to construct()
1093 : *
1094 : * @author greg
1095 : *
1096 : */
1097 : public function __construct()
1098 : {
1099 31 : $args = func_get_args();
1100 31 : $dep = array_pop($args);
1101 31 : $this->PHPDS_dependance($dep);
1102 :
1103 31 : $success = call_user_func_array(array($this, 'construct'), $args);
1104 :
1105 31 : if ($success === false)
1106 31 : throw new PHPDS_Exception('Error constructing an object');
1107 31 : }
1108 :
1109 : /**
1110 : * Empty function called by the actual constructor; meant to be overriden
1111 : *
1112 : * Supposed to return false (exactly) in case of error, otherwise return the object itself
1113 : *
1114 : * @return boolean or object
1115 : */
1116 : public function construct() // variable argument list
1117 : {
1118 28 : return true;
1119 : }
1120 :
1121 : /**
1122 : * Inject the given dependency into $this, and/or returns the owner
1123 : *
1124 : * The default behavior is to try to get the top owner, ie the main PHPDS instance.
1125 : * However the given parmeter is supposed to be the object from where the new object is created.
1126 : * That means you can override this to "catch" the real owner at this time.
1127 : *
1128 : * @date 20110202 (v1.1.1) (greg) added a check to prevent breaking on $this->dependancy if it's not an object
1129 : *
1130 : * @version 1.1.1
1131 : * @author greg
1132 : * @date 20100426
1133 : * @param object $dependance, the "owner", can be either PHPDS or PHPDS_dependant
1134 : * @return owner of $this
1135 : */
1136 : public function PHPDS_dependance($dependance = null)
1137 : {
1138 67 : if (!empty($dependance) && empty($this->dependance)) {
1139 67 : $this->dependance = $dependance;
1140 67 : }
1141 67 : if (!is_object($this->dependance)) {
1142 0 : throw new PHPDS_exception("Dependancy error: dependance is not an object.");
1143 : }
1144 67 : return method_exists($this->dependance, 'PHPDS_dependance') ? $this->dependance->PHPDS_dependance() : $this->dependance;
1145 : }
1146 :
1147 : /**
1148 : * Magic php function, called when trying to access a non-defined field
1149 : *
1150 : * When a method want a field which doesn't exist, we assume it's a data from the father
1151 : *
1152 : * @version 1.0.2
1153 : * @author greg
1154 : * @date 20100805 (v1.0.2) (greg) removed direct access to fields to avoid giving public access to private/protected fields
1155 : * @date 20100426 (v1.0.1) (greg) change to use PHPDS_dependance() to get the proper top owner
1156 : * @param $name string name of the field to fetch
1157 : * @return mixed
1158 : */
1159 : public function __get($name)
1160 : {
1161 : try {
1162 26 : if (!empty($this->parent)) {
1163 0 : if (isset($this->parent->{$name}) || property_exists($this->parent, $name))
1164 0 : return $this->parent->{$name};
1165 0 : }
1166 26 : if ('debug' == $name) return $this->debugInstance();
1167 26 : $top = $this->PHPDS_dependance();
1168 26 : if (!method_exists($top, 'get')) {
1169 0 : throw new Exception("Can't get any '$name' because dependancy is wrong.");
1170 : }
1171 26 : return $top->get($name);
1172 2 : } catch (Exception $e) {
1173 2 : throw new Exception("Can't get any '$name' because dependancy is wrong.");
1174 : }
1175 : }
1176 :
1177 : public function __set($name, $value)
1178 : {
1179 : try {
1180 1 : if (!empty($this->parent)) {
1181 0 : if (isset($this->parent->{$name}) || property_exists($this->parent->{$name}))
1182 0 : $this->parent->{$name} = $value;
1183 0 : }
1184 1 : } catch (Exception $e) {
1185 0 : throw new Exception("Can't set any '$name' because dependancy is wrong.");
1186 : }
1187 1 : }
1188 :
1189 : /**
1190 : * Magic php function used when a non-defined method is called. Here we mimics multi-inheritance by calling methods from "roots"
1191 : *
1192 : * @param string $name
1193 : * @param mixed $arguments
1194 : * @return mixed
1195 : */
1196 : public function __call($name, $arguments)
1197 : {
1198 0 : $root = $this->parent;
1199 0 : if (gettype($root) == 'string') $root = $this->{$root};
1200 0 : if ((gettype($root) == 'object') && method_exists($root, $name)) {
1201 0 : return call_user_func_array(array($root, $name), $arguments);
1202 : }
1203 0 : throw new Exception("No root have a \"$name()\" method.");
1204 : }
1205 :
1206 : /**
1207 : * Create instance of PHPDS_debug.
1208 : *
1209 : * @date 20100412 (v1.0.1) (greg) deal with new sytle naming (starting with PHPDS_)
1210 : * @date 20100426 (v1.0.2) (greg) change to use PHPDS_dependance() to get the proper top owner
1211 : * @date 20110202 (v1.0.3) (greg) fixed a bug of using factoryWith(Ã instead of factory()
1212 : *
1213 : * @version 1.0.3
1214 : * @author greg
1215 : *
1216 : * @return object
1217 : */
1218 : public function debugInstance($domain = null)
1219 : {
1220 1 : if (empty($this->debugInstance)) {
1221 1 : if (empty($domain))
1222 1 : $domain = preg_replace('/^PHPDS_/', '', get_class($this));
1223 1 : $this->debugInstance = $this->factory('PHPDS_debug', $domain);
1224 1 : }
1225 1 : return $this->debugInstance;
1226 : }
1227 :
1228 : /**
1229 : * Send info data to the debug subsystem (console, firebug, ...)
1230 : *
1231 : * The goal of this function is to be called all thourough the code to be able to track bugs.
1232 : *
1233 : * @date 20100928 (v1.1) (greg) this function send data with the DEBUG level
1234 : *
1235 : * @version 1.1
1236 : * @param string $data
1237 : */
1238 : public function log($data)
1239 : {
1240 0 : if (is_a($this, 'debug')) $this->debug($data);
1241 0 : else $this->debugInstance()->debug($data);
1242 0 : }
1243 :
1244 : /**
1245 : * DEPRECATED: alias for log()
1246 : *
1247 : * @param $data
1248 : */
1249 : public function _log($data)
1250 : {
1251 0 : return $this->log($data);
1252 : }
1253 :
1254 : /**
1255 : * DEPRECATED: alias for _log()
1256 : *
1257 : * @param $data
1258 : */
1259 : public function info($data)
1260 : {
1261 0 : $this->_log($data);
1262 0 : }
1263 :
1264 : /**
1265 : * Create a new instance of the given class and link it as dependant (variable number of argument)
1266 : *
1267 : * @param string $classname, ... name of the class to instanciate, followed by class specific parameters
1268 : *
1269 : * @date 20100922 (v1.2) (greg) created factoryWith()
1270 : * @date 20100217 Split into factory() and PHPDS._factory() so the latter can be called with an array of arguments
1271 : * @date 20091125 Added the dependance link as the last parameter of the constructor
1272 : *
1273 : * @version 1.2
1274 : * @author greg
1275 : *
1276 : * @see PHPDS_debug
1277 : *
1278 : * @return instance of $classname
1279 : */
1280 : public function factory($classname) // actually more parameters can be given
1281 : {
1282 21 : $params = func_get_args();
1283 21 : array_shift($params);
1284 :
1285 21 : return $this->factoryWith($classname, $params);
1286 : }
1287 :
1288 : /**
1289 : * Create a new instance of the given class and link it as dependant (variable number of argument)
1290 : *
1291 : * @param string $classname name of the class to instanciate
1292 : * @param array $params all the parameters to feed the object's constructor (packed into an array)
1293 : *
1294 : * @version 1.0
1295 : * @author greg
1296 : *
1297 : * @return instance of $classname
1298 : */
1299 : public function factoryWith($classname, $params)
1300 : {
1301 21 : $father = $this->PHPDS_dependance();
1302 : // support for plugin aliasing
1303 21 : if (!empty($father->PluginClasses[$classname]['class_name'])) {
1304 0 : $classname = $father->PluginClasses[$classname]['class_name'];
1305 0 : }
1306 :
1307 21 : return $father->_factory($classname, $params, $this);
1308 : }
1309 : }
1310 :
1311 : /**
1312 : * Turn core arrays into objects.
1313 : *
1314 : */
1315 : class PHPDS_array extends ArrayObject
1316 : {
1317 :
1318 : /**
1319 : * Magig set array.
1320 : *
1321 : * @param string $name
1322 : * @param string $val
1323 : */
1324 : public function __set($name, $val)
1325 : {
1326 1 : $this[$name] = $val;
1327 1 : }
1328 :
1329 : /**
1330 : * Magic get array.
1331 : *
1332 : * @param string $name
1333 : * @return string
1334 : */
1335 : public function __get($name)
1336 : {
1337 1 : return (isset($this[$name]) ? $this[$name] : null);
1338 : }
|