deduplicate session cookies
[pear] / HTML / FlexyFramework2.php
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
3 // +----------------------------------------------------------------------+
4 // | PHP Version 4                                                        |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2002 The PHP Group                                |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 2.02 of the PHP license,      |
9 // | that is bundled with this package in the file LICENSE, and is        |
10 // | available at through the world-wide-web at                           |
11 // | http://www.php.net/license/2_02.txt.                                 |
12 // | If you did not receive a copy of the PHP license and are unable to   |
13 // | obtain it through the world-wide-web, please send a note to          |
14 // | license@php.net so we can mail you a copy immediately.               |
15 // +----------------------------------------------------------------------+
16 // | Authors:  Alan Knowles <alan@akbkhome.com>                           |
17 // +----------------------------------------------------------------------+
18 //
19 // $Id: FlexyFramework.php,v 1.8 2003/02/22 01:52:50 alan Exp $
20 //
21 //  Description
22 //  A Page (URL) to Object Mapper
23 //  Cleaned up version.. - for use on new projects -- not BC!! beware!!!
24
25
26 //-----------------------------------------------------------
27 // Debian APACHE - some idiot disabled AcceptPathInfo - it needs adding back.
28 //-----------------------------------------------------------
29
30  
31  
32 // Initialize Static Options
33 require_once 'HTML/FlexyFramework2/Page.php';  
34
35
36 // To be removed ?? or made optional or something..
37  
38
39 // remove E_ANAL  
40  
41 error_reporting(E_ALL & ~E_STRICT );
42 //ini_set('display_errors','off');
43 //ini_set('log_errors','off');
44
45
46
47
48
49
50 /**
51 * The URL to Object Mapper
52 *
53 * Usage:
54 * Create a index.php and add these lines.
55 *  
56 * ini_set("include_path", "/path/to/application"); 
57 * require_once 'HTML/FlexyFramework.php';
58 * HTML_FlexyFramework::factory(array("dir"=>"/where/my/config/dir/is");
59 *
60 *
61 * the path could include pear's path, if you dont install all the pear 
62 * packages into the development directory.
63 *
64 * It attempts to load a the config file from the includepath, 
65 * looks for ConfigData/default.ini
66 * or ConfigData/{hostname}.ini
67 * if your file is called staging.php rather than index.php 
68 * it will try staging.ini
69 *
70 */
71  
72 class HTML_FlexyFramework2 {
73     
74     /**
75      * Confirgurable items..
76      * If we set them to 'true', they must be set, otherwise they are optional.
77      */
78     var $project; // base class name
79     var $database; // set to true even if nodatabase=true
80     
81     // optional
82     var $debug = false;
83     var $enable = false; // modules
84     var $disable = false; // modules or permissions
85     var $appName = false;
86     var $appNameShort = false; // appname (which has templates)
87     var $version = false; // give it a version name. (appended to compile dir)
88     var $nodatabase = false; // set to true to block db config and testing.
89     var $fatalAction = false; // page to redirct to on failure. (eg. databse down etc.)
90     var $charset = false; // default UTF8
91     var $dataObjectsCache = true;  // use dataobjects ini cache.. - let's try this as the default behaviour...
92     var $dataObjectsCacheExpires = 72000; // 20 hours..
93     var $languages = false; // language settings -- see _handlelanguage
94     var $projectExtends = false; // if this is an array, it's a fallback of 'Projects' that can be called
95     var $initOnly = false; // use to prevent run() being called...
96                 // use $this->run($_SERVER['REQUEST_URI'],false); to manually boot it..
97
98     
99     // derived.
100     var $cli = false; // from cli 
101     var $run = false; // from cli
102     var $enableArray = false; // from enable.
103     var $classPrefix = false; // from prject.
104     var $baseDir = false ; // (directory+project)
105     var $rootDir = false ; // (directory that index.php is in!)
106     
107     var $baseURL = false;
108     var $rootURL = false ; // basename($baseURL)
109     
110     var $page = false; // active page..
111     var $timer = false; // the debug timer
112     var $calls = false; // the number of calls made to run!
113     var $start = false; // the start tiem.
114     
115     var $baseRequest = '';
116     var $ext; // the striped extention.
117     
118     var $dataObjectsOriginalIni = ''; // 1 houre..
119     
120     // used to be $_GLOBALS[__CLASS__]
121     
122     static $singleton; 
123     
124     
125     /**
126      * 
127      * Constructor - with assoc. array of props as option
128      * called by index.php usually, and runs the app code,
129      *
130      * uses 'universal construcor' format, so the argument relates directly to properties of this object.
131      * 
132      */
133     
134     
135     function __construct($config)
136     {
137         if (isset(self::$singleton)) {
138             trigger_error("FlexyFramework Construct called twice!", E_ERROR);
139         }
140         
141         self::$singleton = $this;
142         
143         $this->calls = 0;
144
145         $m = explode(' ',microtime());
146         $this->start = $m[0] + $m[1];
147         
148         $config = $this->loadModuleConfig($config);
149         
150         foreach($config as $k=>$v) {
151             $this->$k = $v;
152         }
153         $this->_parseConfig();
154         
155         // echo '<PRE>'; print_r($this);exit;
156         if ($this->cli) {
157             $args = $_SERVER['argv'];
158             array_shift($args );
159             array_shift($args );
160             $this->_run($this->run,false,$args);
161             return;
162         }
163     
164         // handle apache mod_rewrite..
165         // it looks like this might not work anymore..
166         
167         if ($this->initOnly) {
168             return;
169         }
170         
171         
172         if (!empty($_SERVER['REDIRECT_URL'])) {
173             
174             $this->_run($_SERVER['SCRIPT_NAME'] . $_SERVER['REQUEST_URI'],false);
175             return ;
176         }
177         
178         
179         $this->_run($_SERVER['REQUEST_URI'],false);
180             
181         
182     }
183     /**
184      * This is the standard way to get information about the application settings.
185      * $ff = HTML_FlexyFramework::get();
186      * if ($ff->SomeVar[...])....
187      *
188      */
189     static function get()
190     {
191         return self::$singleton;
192     }
193     /*
194      * looks for files in the path and load up the default values for config?
195      */
196     function loadModuleConfig($cfg)
197     {
198         
199         $mods = empty($cfg['enable']) ? array() : explode(',',$cfg['enable']);
200         array_unshift($mods,'');
201         $proj = $cfg['project'];
202         $rootDir = realpath(dirname($_SERVER["SCRIPT_FILENAME"]));
203
204         foreach($mods as $m) {
205             $cls = $proj. (strlen($m) ? '_'. $m  : '' ) . '_Config';
206
207             if (!file_exists($rootDir . '/'.str_replace('_','/', $cls). '.php')) {
208                 continue;
209             }
210             require_once str_replace('_','/', $cls). '.php';
211             $c = new $cls();
212             if (method_exists($c,'init')) {
213                 $cfg = $c->init($this,$cfg);
214             }
215         }
216         return $cfg;
217     }
218     
219   
220     /**
221      * parse the configuration set by the constructor.
222      * 
223      *
224      */
225   
226     function _parseConfig()
227     {
228         
229         // make sure required values are set.. (anything that is not defaulted to false..)
230         foreach(get_class_vars(__CLASS__) as $k =>$v) {
231             if ($v === false && !isset($this->$k)) {
232                 die("$k is not set");
233             }
234         }
235         
236         $this->_handleLanguages();
237         
238         // enable modules.
239         if (!empty($this->enable)) {
240             $this->enableArray = explode(',', $this->enable);
241             
242             if (!in_array('Core',$this->enableArray ) &&
243                 !in_array('Core', explode(',', $this->disable ? $this->disable : '')))
244             {
245                 $this->enable = 'Core,'. $this->enable ;
246                 $this->enableArray = explode(',', $this->enable);
247             }
248         }
249         // are we running cli?
250         $this->cli = php_sapi_name() == 'cli'; 
251         
252         // will these work ok with cli?
253         $bits = explode(basename($_SERVER["SCRIPT_FILENAME"]), $_SERVER["SCRIPT_NAME"]);
254         if (!$this->cli) {
255             $bits[0] = str_replace('%2F','/',urlencode($bits[0]));
256             $this->baseURL = $bits[0] . basename($_SERVER["SCRIPT_FILENAME"]);
257             
258             if (empty($_SERVER['SCRIPT_NAME'])) {
259                 $this->baseURL = ''; // ??? this is if we replace top level...
260             }
261         }
262         // if cli - you have to have set baseURL...
263         
264         
265         $this->rootDir = realpath(dirname($_SERVER["SCRIPT_FILENAME"]));
266         $this->baseDir = $this->rootDir .'/'. $this->project;
267         $this->rootURL = dirname($this->baseURL);
268         $this->rootURL = ($this->rootURL == '/') ? '' : $this->rootURL;
269          
270       
271         //var_dump($this->baseURL);
272         
273         if (!isset($this->database) && isset($this->PDO_DataObject['database'])) {
274             $this->database = $this->PDO_DataObject['database'];
275         }
276         
277          $this->classPrefix   = str_replace('/', '_', $this->project) . '_';
278         
279         // list the available options..
280         if ($this->cli && empty($_SERVER['argv'][1])) {
281             require_once 'HTML/FlexyFramework2/Cli.php';
282             $fcli = new HTML_FlexyFramework2_Cli($this);
283             $fcli->cliHelp();
284             exit;
285         }
286         
287         
288         // see if it's a framework assignment.
289         $ishelp = false;
290         if ($this->cli) {
291             require_once 'HTML/FlexyFramework2/Cli.php';
292             $fcli = new HTML_FlexyFramework2_Cli($this);
293             $res = $fcli->parseDefaultOpts();
294             if ($res === true) {
295                 $ishelp = true;
296             } 
297              
298         }
299         
300         
301         $this->run = $this->cli ? $_SERVER['argv'][1] : false;
302      
303         
304         $this->_parseConfigDataObjects();
305         if ($this->dataObjectsCache && !$this->nodatabase) {
306             $this->_configDataObjectsCache();
307         }
308         
309         $this->_parseConfigTemplate();
310         $this->_parseConfigMail();
311  
312         //echo '<PRE>';print_r($this);exit;
313         
314         //$this->_exposeToPear();
315                 
316
317         $this->_validateEnv();
318         
319         if ($ishelp) {
320             return;
321         }
322
323         $this->_validateDatabase();
324  
325         $this->_validateTemplate();
326         
327     }
328     /**
329      *
330      *
331      *'languages' => array(
332             'param' => '_lang',
333             'avail' => array('en','zh_HK', 'zh_CN'),
334             'default' => 'en',
335             'cookie' => 'TalentPricing_lang',
336             'localemap' => array(
337                 'en' => 'en_US.utf8',
338                 'zh_HK' => 'zh_TW.utf8',
339                 'zh_CN' => 'zh_CN.utf8',
340             )
341         ),
342     */
343     
344     function _handleLanguages()
345     {
346         if (
347                 empty($this->languages) ||
348                 (
349                         !isset($this->languages['cookie']) && !isset($this->languages['default'])
350                 )
351         ) {
352             return;
353         }
354         
355         $cfg = $this->languages;
356         
357         $default = $cfg['default'];
358         
359         if(!empty($_SERVER["HTTP_ACCEPT_LANGUAGE"])){
360             
361             $brower_langs = explode(",", $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
362             
363             foreach ($brower_langs as $bl) {
364                 $l = preg_replace('/;(.*)/', '', $bl);
365                 
366                 $l = str_replace('-', '_', $l);
367                 
368                 if(!in_array($l, $cfg['avail'])){
369                     continue;
370                 }
371                 
372                 $default = $l;
373                 break;
374             }
375         }
376            
377         $lang = isset($_COOKIE[$cfg['cookie']]) ?  $_COOKIE[$cfg['cookie']] : $default;
378
379         if (isset($_REQUEST[$cfg['param']])) {
380             $lang = $_REQUEST[$cfg['param']];
381         }
382     
383         if (!in_array($lang, $cfg['avail'])) {
384             $lang = $cfg['default'];
385         }
386         if (isset($cfg['localemap'][$lang])) {
387             setlocale(LC_ALL, $cfg['localemap'][$lang]);
388         }
389         setcookie($cfg['cookie'], $lang, 0, '/');
390         
391         $this->locale = $lang;
392         
393         if (!empty($this->HTML_Template_Flexy)) {
394             $this->HTML_Template_Flexy['locale'] = $lang;   //set a language for template engine
395         }
396          
397     }
398     
399     /**
400      * overlay array properties..
401      */
402     
403     function applyIf($prop, $ar) {
404         if (!isset($this->$prop)) {
405             $this->$prop = $ar;
406             return;
407         }
408         // add only things that where not set!!!.
409         $this->$prop = array_merge($ar,$this->$prop);
410         
411         return;
412         //foreach($ar as $k=>$v) {
413         //    if (!isset($this->$prop->$k)) {
414          //       $this->$prop->$k = $v;
415           //  }
416        // }
417     }
418     
419     /**
420      * DataObject cache 
421      * - if turned on (dataObjectsCache = true) then 
422      *  a) ini file points to a parsed version of the structure.
423      *  b) links.ini is a merged version of the configured link files.
424      * 
425      * This only will force a generation if no file exists at all.. - after that it has to be called manually 
426      * from the core page.. - which uses the Expires time to determine if regeneration is needed..
427      * 
428      * 
429      */
430     
431     function _configDataObjectsCache()
432     {
433         // cli works under different users... it may cause problems..
434         $this->debug(__METHOD__);
435         if (function_exists('posix_getpwuid')) {
436             $uinfo = posix_getpwuid( posix_getuid () ); 
437             $user = $uinfo['name'];
438         } else {
439             $user = getenv('USERNAME'); // windows.
440         }
441         
442         
443
444         $iniCache = ini_get('session.save_path') .'/' . 
445                'pdocfg-' . $user . '/'. str_replace('/', '_', $this->project) ;
446         
447         
448         if ($this->appNameShort) {
449             $iniCache .= '_' . $this->appNameShort;
450         }
451         if ($this->version) {
452             $iniCache .= '.' . $this->version;
453         }
454         if ($this->database === false) {
455             return;
456         }
457         
458         $dburl = parse_url($this->database);
459         if (!empty($dburl['path'])) {
460             $iniCache .= '-'.ltrim($dburl['path'],'/');
461         }
462         
463         $iniCache .= '.ini';
464         $this->debug(__METHOD__ . " : ini cache : $iniCache");
465         
466         $dburl = parse_url($this->database);
467          //override ini setting... - store original..
468         
469         if (isset($this->PDO_DataObject['schema_location'])) {
470             $this->dataObjectsOriginalIni = $this->PDO_DataObject['schema_location'];
471             ///print_r($this->DB_DataObject);exit;
472         }
473         // 
474         $this->PDO_DataObject['schema_location']   = $iniCache;
475         PDO_DataObject::config($this->PDO_DataObject);
476         
477         // we now have the configuration file name..
478          
479         if (!file_exists($iniCache) || empty( $this->dataObjectsCacheExpires)) {
480             $this->generateDataobjectsCache(true);
481             return;
482         }
483      
484         
485         
486     }
487     /**
488      * generateDataobjectsCache:
489      *
490      * if the 'cache file' does not exist, then
491      * create it using HTML_FlexyFramework2_Generator
492      * 
493      * create xxx.ini and xxx.links.ini 
494      * 
495      * @arg force (boolean) force generation - default false;
496      * 
497      */
498      
499     function generateDataobjectsCache($force = false)
500     {
501         //$this->debug('generateDataobjectsCache: force=' . ($force ? 'yes' : 'no'));
502         if (!$this->dataObjectsCache) { // does not use dataObjects Caching..
503              PDO_DataObject::config('schema_location', $this->dataObjectsOriginalIni );
504             
505             $this->debug('generateDataobjectsCache', 'dataObjectsCache - empty');
506             return;
507         }
508         
509         $dburl = parse_url($this->database);
510         $dbnick =  basename($dburl['path']);
511         
512         
513        
514         $iniCache = $this->PDO_DataObject['schema_location'];
515         
516         if ($force && file_exists($iniCache)) {
517             $files = glob(dirname($iniCache).'/*.ini');
518             foreach($files as $f) {
519                 unlink($f);
520             } 
521             clearstatcache();
522         }
523         
524         $iniCacheTmp = $iniCache . '.tmp' .md5(rand());  // random to stop two processes using the same file.
525         // has it expired..
526         $force = ($force ? $force : !file_exists($iniCache)) || !$this->dataObjectsCacheExpires;
527         // $this->debug('generateDataobjectsCache: after check : force=' . ($force ? 'yes' : 'no'));
528          // not force or not expired, do not bother..
529         if (!$force) {
530             if ((filemtime($iniCache) + $this->dataObjectsCacheExpires) >time()) {
531                 return;
532             }
533         }
534         
535         
536         $this->debug('generateDataobjectsCache', 'dataObjectsCache  generating');
537          //echo "GENERATE?";
538         
539         // force quoting of column names..
540         // unless it forced off..
541         if (!isset($this->PDO_DataObject['quote_identifiers'] )) { 
542             $this->PDO_DataObject['quote_identifiers'] = true;
543         }
544         if (!file_exists(dirname($iniCache))) {
545             mkdir(dirname($iniCache),0700, true);
546         }
547         
548         $this->PDO_DataObject['schema_location'] = $iniCacheTmp;
549         
550         PDO_DataObject::reset();
551         
552         PDO_DataObject::config($this->PDO_DataObject);
553         
554         
555         
556         // DB_DataObject::debugLevel(1);      
557         require_once 'HTML/FlexyFramework2/Generator.php';
558         
559         PDO_DataObject::config('database', $this->database);
560         $generator = new HTML_FlexyFramework2_Generator();
561         $generator->start();
562         
563         $this->debug('generateDataobjectsCache', 'dataObjectsCache  writing');
564         HTML_FlexyFramework2_Generator::writeCache($iniCacheTmp, $iniCache); 
565         // reset the cache to the correct lcoation.
566         PDO_DataObject::config('schema_location',  $iniCache);
567         $this->PDO_DataObject['schema_location'] = $iniCache;
568         //$this->_exposeToPear();
569         
570         //$GLOBALS['_DB_DATAOBJECT']['INI'][$this->database] =   parse_ini_file($iniCache, true);
571         //$GLOBALS['_DB_DATAOBJECT']['SEQUENCE']
572         // clear any dataobject cache..
573          
574         
575         //die("done");
576         
577     }
578     /**
579      * DataObject Configuration:
580      * Always in Project/DataObjects
581      * unless enableArray is available...
582      * 
583      * 
584      * 
585      */
586     function _parseConfigDataObjects()
587     {
588         if ($this->nodatabase && !$this->database) {
589             return;
590         }
591         
592         // better done here.. ?? only include if we have database configuration?
593         require_once 'PDO/DataObject.php';
594         
595         $dburl = parse_url($this->database);
596         $dbini = 'ini_'. basename($dburl['path']);
597                 
598         $dbinis =  array(); //array(dirname(__FILE__) . '/Pman/DataObjects/pman.ini');
599         $dbreq =  array(); //array( dirname(__FILE__) . '/Pman/DataObjects/');
600         $dbcls =  array(); //array('Pman_DataObjects_');
601
602         $project = explode('/',$this->project)[0]; 
603         
604         if (!empty($this->enableArray)) {
605                 
606             $tops = array_merge( array($project), empty($this->projectExtends) ? array() : $this->projectExtends);
607             
608             foreach($tops as $td) {
609                  $bd = $this->rootDir .'/'.$td;
610                 foreach($this->enableArray as $m) {
611                     // look in Pman/MODULE/DataObjects/*
612                      if (file_exists($bd.'/'.$m.'/DataObjects')) {
613                         $dbinis[] = $bd.'/'.$m.'/DataObjects/'. strtolower($td).'.ini';
614                         $dbcls[] = $td.'_'. $m . '_DataObjects_';
615                         $dbreq[] = $bd.'/'.$m.'/DataObjects';
616                         continue;
617                     }
618                     // look in MODULE/DataObjects ?? DO WE SUPPORT THIS ANYMORE???
619                     if (file_exists($bd.'/../'.$m.'/DataObjects')) {
620                         $dbinis[] = $bd.'/../'.$m.'/DataObjects/'. strtolower($td).'.ini';
621                         $dbcls[] = $td. '_DataObjects_';
622                         $dbreq[] = $bd.'/../'.$m.'/DataObjects';
623                     }
624                         
625                         
626                       
627                 }
628             }     
629         } else {
630             
631             if (isset($this->PDO_DataObject['schema_location'])) {
632                 $dbinis[] = $this->PDO_DataObject['schema_location'] .'/'.basename($dburl['path']).'.ini';
633             } else {
634                 $dbinis[] = $this->baseDir.'/DataObjects/'.basename($dburl['path']).'.ini';
635             }
636             // non modular.
637             
638             $dbcls[] = $project .'_DataObjects_';
639             $dbreq[] = $this->baseDir.'/DataObjects';
640         }
641             
642         
643         $this->applyIf('PDO_DataObject', array(   
644         
645             'class_location' =>  implode(PATH_SEPARATOR,$dbreq),
646             'class_prefix' =>  implode(PATH_SEPARATOR,$dbcls),
647             'database'        => $this->database,    
648             ///'require_prefix' => 
649          //   'schema_location' => dirname(__FILE__) . '/Pman/DataObjects/',
650              'schema_location' => implode(PATH_SEPARATOR,$dbinis),
651          
652            //   'debug' => 5,
653         ));
654         PDO_DataObject::config($this->PDO_DataObject);
655         
656     }
657     /**
658      Set up thetemplate
659      * 
660      */
661     function _parseConfigTemplate()
662     {
663         
664         // compile.
665         if (function_exists('posix_getpwuid')) {
666             $uinfo = posix_getpwuid( posix_getuid () ); 
667          
668             $user = $uinfo['name'];
669         } else {
670             $user = getenv('USERNAME'); // windows.
671         }
672         
673         $compileDir = ini_get('session.save_path') .'/' . 
674             $user . '_compiled_templates_' . $this->project;
675         
676         if ($this->appNameShort) {
677             $compileDir .= '_' . $this->appNameShort;
678         }
679         if ($this->version) {
680             $compileDir .= '.' . $this->version;
681         }
682         
683         // templates. -- all this should be cached!!!
684         $src = array();
685          
686         
687         if ($this->appNameShort && !in_array('Core', explode(',', $this->disable ? $this->disable : ''))) {
688             // in app based version, template directory is in Core
689             
690             $src = array(  
691                 $this->baseDir . '/Core/templates'
692             );
693         }
694         
695         if(!empty($this->projectExtends)){
696             foreach ($this->projectExtends as $e){
697                 $add = $this->rootDir . '/' . $e .'/templates';
698                 if (!in_array($add,$src) && file_exists($add)) {
699                     $src[] = $add;
700                 }
701             }
702         }
703         
704         $src[] = $this->baseDir . '/templates';
705         
706         
707         
708         if (!empty($this->enableArray)) {
709              
710             
711             foreach($this->enableArray as $m) {
712                 $add = $this->baseDir . '/' . $m .'/templates';
713                 if (!in_array($add,$src) && file_exists($add) && $this->appNameShort != $m) {
714                     $src[] = $add;
715                 }
716                 
717             }
718             if (!empty($this->projectExtends)  )  {
719                 foreach ($this->projectExtends as $extend){
720                     foreach($this->enableArray as $m) {
721                         $add = $this->rootDir . '/' . $extend . '/' . $m .'/templates';
722                         if (!in_array($add,$src) && file_exists($add) && $this->appNameShort != $m) {
723                             $src[] = $add;
724                         }
725                     }
726                 }
727     
728             }
729         }
730          
731         
732         if ($this->appNameShort) {
733             $src[] =  $this->baseDir . '/'. $this->appNameShort. '/templates';
734         }
735         
736         // images may come from multiple places: - if we have multiple template directories.?
737         // how do we deal with this..?
738         // images/ << should always be mapped to master!
739         // for overridden appdir ones we will have to se rootURL etc.
740         
741         $url_rewrite = 'images/:'. $this->rootURL . '/'. $this->project. '/templates/images/';
742         
743         $this->applyIf('HTML_Template_Flexy', array(
744             'templateDir' => implode(PATH_SEPARATOR, $src),
745             'compileDir' => $compileDir,
746             'multiSource' => true,
747             'forceCompile' => 0,
748             'url_rewrite' => $url_rewrite,
749             'filters' => 'Php,SimpleTags', /// for non-tokenizer version?
750             'debug' => $this->debug ? 1 : 0,
751             'useTokenizer' => 1,
752              
753             
754         
755         
756         ));
757     } 
758     
759     function _parseConfigMail()
760     {
761         $this->applyIf('HTML_Template_Flexy', array(
762            'debug' => 0,
763            'driver' => 'smtp',
764            'host' => 'localhost',
765            'port' => 25,
766         ));
767     }
768     
769     /**
770      * exposes to PEAR::getStaticProperty..
771      * at present, only FlexyFramework uses this..
772      * // we should really stop it..
773      * ctor for Flexy - should read from config...
774      */
775     /*
776     function _exposeToPear()
777     {
778         $cls = array_keys(get_class_vars(__CLASS__));
779         $base = array();
780         
781         // anything that get's set, that's not in our default properties
782         // is assumed to be an option set .
783         foreach(get_object_vars($this) as $k=>$v) {
784             if (in_array($k,$cls)) {
785                 $base[$k] = $v;
786                 continue;
787             }
788             $options = &PEAR::getStaticProperty($k,'options');
789             $options = $v;
790         }
791        // $options = &PEAR::getStaticProperty('HTML_FlexyFramework2','options');
792        // $options = $base;
793          //   apply them..
794     }
795     */
796     
797     
798     function _validateEnv() 
799     {
800         /* have I been initialized */
801         
802         /* 
803         if (get_magic_quotes_gpc() && !$this->cli) {
804             $this->fatalError(
805                 "magic quotes is enabled add the line<BR>
806                    php_value magic_quotes_gpc 0<BR>
807                    to your .htaccess file <BR>
808                    (Apache has to be configured to &quot;AllowOverride Options AuthConfig&quot; for the directory)
809                    ");
810                 
811         }
812         */
813         // set up error handling - 
814         //$this->error = new HTML_FlexyFramework2_Error();
815         
816         /// fudge work around bugs in PEAR::setErrorHandling(,)
817         //$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_CALLBACK;
818         //$GLOBALS['_PEAR_default_error_options'] = array($this->error,'raiseError');
819         
820         
821         
822         if ($this->debug) {
823             require_once 'Benchmark/Timer.php'; 
824             $this->timer = new BenchMark_Timer(true);
825             register_shutdown_function(function() { echo $this->timer->getOutput(); });
826         }
827
828     }
829     
830     function _validateDatabase()
831     {
832         //echo "<PRE>"; print_r($this);
833
834         if ($this->nodatabase) {
835             return;
836         }
837         
838         // database is the only setting - we dont support mult databses?
839           
840             
841         $x = new PDO_Dataobject();
842         PDO_Dataobject::config('database', $this->database);
843         try {
844             $x->PDO();
845         } catch (Exception $e) {
846                                 
847
848                 $this->fatalError("Configuration or Database Error: could not connect to Database, <BR>
849                     Please check the value given to HTML_FlexyFramework2, or run with debug on!<BR>
850                      <BR> ".$e->getMessage());
851         }
852         
853         
854         
855     }
856     function _validateTemplate()
857     {
858         // check that we have a writeable directory for flexy's compiled templates.
859         
860         if (empty($this->HTML_Template_Flexy['compileDir'])) {
861             return;
862         }
863         
864         if ( !file_exists($this->HTML_Template_Flexy['compileDir']))  {
865             mkdir($this->HTML_Template_Flexy['compileDir'], 0700, true);
866             clearstatcache();
867              
868             if ( !file_exists($this->HTML_Template_Flexy['compileDir']))  {
869             
870                 $this->fatalError("Configuration Error: you specified a directory that does not exist for<BR>
871                     HTML_Template_Flexy => compileDir  {$this->HTML_Template_Flexy['compileDir']}<BR>\n"
872                 );
873             }
874         }
875         
876         if (!is_writeable($this->HTML_Template_Flexy['compileDir'])) {
877             $this->fatalError("Configuration Error: Please make sure the template cache directory is writeable<BR>
878                     eg. <BR>
879                     chmod 700 {$this->HTML_Template_Flexy['compileDir']}<BR>
880                     chgrp apache_user  {$this->HTML_Template_Flexy['compileDir']}<BR>\n"
881             );
882         }
883         //echo "<PRE>";print_R($config);
884         
885         
886          
887           
888         
889         
890     }
891   
892   
893    
894         
895     
896     
897     /**
898     * Quality Redirector
899     *
900     * Usage in a page.:
901     * HTML_FlexyFramework::run('someurl/someother',array('somearg'=>'xxx'));
902     * ...do clean up...
903     * exit; <- dont cary on!!!!
904     *
905     * You should really
906     * 
907     * @param   string           redirect to url 
908     * @param   array Args Optional      any data you want to send to the next page..
909     * 
910     *
911     * @return   false
912     * @access   public
913     * @static
914     */
915   
916     
917     static function run($request,$args=array()) 
918     {
919         self::$singleton->_run($request,true,$args);
920         return false;
921     }
922     
923     /**
924      * initPage - load the page up, and set the variables, but do not actually run it.
925      * - used where we want features from this framework, buy may actually be running another one..
926      *
927      * call with ($_SERVER["SCRIPT_NAME"]) for root page...
928      * 
929      */
930     public function initPage($request, $isRedirect=false, $args=array())
931     {
932         $newRequest = $this->_getRequest($request,$isRedirect);
933         
934         // find the class/file to load
935         list($classname,$subRequest) = $this->requestToClassName($newRequest,FALSE);
936         
937         
938         $this->debug("requestToClassName return = CLASSNAME: $classname SUB REQUEST: $subRequest");
939         
940         // assume that this was handled by getclassname ?????
941         if (!$classname) {
942             return false;
943         }
944         
945         // make page data/object accessable at anypoint in time using  this
946         // not sure if this is used anymore - or even works..?
947         
948         $classobj =  new  $classname();  // normally do not have constructors.
949         
950         $classobj->baseURL = $this->baseURL;
951         $classobj->rootURL = $this->rootURL;
952         $classobj->rootDir = $this->rootDir;
953         $classobj->bootLoader  = $this;
954         $classobj->request = $newRequest;
955         $classobj->timer = &$this->timer;
956         $classobj->subrequest = $subRequest;
957          
958         $this->page = $classobj;
959         return $classobj;
960     }
961     
962     
963     /**
964     * The main execution loop
965     *
966     * recursivly self called if redirects (eg. return values from page start methods)
967     * 
968     * @param   string from $_REQUEST or redirect from it'self.
969     * @param   boolean isRedirect  = is the request a redirect  TRUE = always handle as 'GET', 1 = handle as POST/GET depending on method, 0 = handle as initial request.
970     *
971     *
972     * @return   false || other    false indicates no page was served!
973     * @access   public|private
974     * @see      see also methods.....
975     */
976   
977     public function _run($request,$isRedirect = false,$args = array()) 
978     {
979         
980         // clean the request up.
981         $this->calls++;
982         
983         if ($this->calls > 5) {
984             // to many redirections...
985             trigger_error("FlexyFramework:: too many redirects - backtrace me!",E_USER_ERROR);
986             exit;
987         }
988         
989         $classobj  = $this->initPage($request,$isRedirect,$args);
990         $classobj->cli = $this->cli;
991         
992         if ($this->cli && !$isRedirect ) { // redirect always just takes redirect args..
993             require_once 'HTML/FlexyFramework2/Cli.php';
994             $fcli = new HTML_FlexyFramework2_Cli($this);
995             $nargs = $fcli->cliParse(get_class($classobj));
996             $args = $nargs === false ? $args : $nargs; /// replace if found.
997             $classobj->cli_args = $nargs;
998         }
999         
1000         // echo '<PRE>'; print_r($this);exit;
1001         // echo "CHECK GET AUTH?";
1002         if (!method_exists($classobj, 'getAuth')) {
1003         //    echo "NO GET AUTH?";
1004             $this->fatalError("class ". get_class($classobj) . " does not have a getAuth Method");
1005             return false;
1006         }
1007         
1008         /* check auth on the page */
1009         if (is_string($redirect = $classobj->getAuth())) {
1010             $this->debug("GOT AUTH REDIRECT".$redirect);
1011             return $this->_run($redirect,TRUE);
1012         }
1013         // used HTML_FlexyFramework::run();
1014                  
1015
1016         if ($redirect === false) {
1017             $this->debug("GOT AUTH FALSE");    
1018             return false; /// Access deined!!! - 
1019         }
1020      
1021         // allow the page to implement caching (for the full page..)
1022         // although normally it should implement caching on the outputBody() method.
1023         
1024         if (method_exists($classobj,"getCache")) {
1025             if ($result = $classobj->getCache()) {
1026                 return $result;
1027             }
1028         }
1029         /* allow redirect from start */
1030         if (method_exists($classobj,"start")) {
1031             if (is_string($redirect = $classobj->start($classobj->subrequest,$isRedirect,$args)))  {
1032                 $this->debug("REDIRECT $redirect <BR>");
1033                 return $this->_run($redirect,TRUE);
1034             }
1035             if ($redirect === false) {
1036                 return false;
1037             }
1038         }
1039                 
1040
1041          // used HTML_FlexyFramework::run();
1042         
1043         /* load the modules 
1044          * Modules are common page components like navigation headers etc.
1045          * that can have dynamic code.
1046          * Code has been removed now..
1047          */
1048         
1049         
1050         if ($this->timer) {
1051             $this->timer->setMarker("After $request loadModules Modules"); 
1052         }
1053         
1054         /* output it  - (our base page does not implement output for cli. */
1055         
1056         if ( method_exists($classobj,'output')) {
1057             $classobj->output(); 
1058         }
1059         
1060         
1061         if ($this->timer) {
1062             $this->timer->setMarker("After $request output"); 
1063             $this->timer->stop(); //?? really - yes...
1064            
1065             
1066         }
1067         
1068         if ($this->cli) {
1069             return true;
1070         }
1071         
1072         
1073         exit; /// die here...
1074         
1075     }
1076     
1077     /**
1078     * map the request into an object and run the page.
1079     *
1080     * The core of the work is done here.
1081     * 
1082     * 
1083     * @param   request  the request string
1084     * @param   boolean isRedirect - indicates that it should not attempt to strip the .../index.php from the request.
1085     * 
1086     * @access  private
1087     */
1088   
1089     function _getRequest($request, $isRedirect) 
1090     {
1091         
1092         
1093         
1094         if ($this->cli) {
1095             return $request;
1096         }
1097         
1098         $startRequest = $request;
1099         $request =@ array_shift(explode('?', $request));
1100         $this->debug("INPUT REQUEST $request<BR>");
1101         if (!$isRedirect) {
1102             // check that request forms contains baseurl????
1103              
1104             $subreq = substr($request,0,strlen($this->baseURL));
1105             if ($subreq != substr($this->baseURL,0,strlen($subreq))) {
1106                 $this->fatalError(
1107                     "Configuration error: Got base of $subreq which does not 
1108                         match configuration of: $this->baseURL} ");
1109             }
1110             $request = substr($request,strlen($this->baseURL));
1111              
1112         }
1113         // strip front
1114         // echo "REQUEST WAS: $request<BR>";
1115         // $request = preg_replace('/^'.preg_quote($base_url,'/').'/','',trim($request));
1116         // echo "IS NOW: $request<BR>";
1117         // strip end
1118         // strip valid html stuff
1119         //$request = preg_replace('/\/[.]+/','',$request);
1120         
1121
1122         $request = preg_replace('/^[\/]*/','',$request);
1123         $request = preg_replace('/\?.*$/','',$request);
1124         $request = preg_replace('/[\/]*$/','',$request);
1125         $this->baseRequest = $request;
1126         $request = str_replace('&','',$request); // any other invalid characters???
1127         $request = preg_replace('/\.([a-z]+)$/','',$request);
1128         $this->ext = substr($this->baseRequest , strlen($request));
1129         
1130         // REDIRECT ROO to index.php! for example..
1131         
1132         // this has a slight issue when we are using 'hidden index.php' mod_rewrite..
1133         
1134         /*
1135          normal (non-mod_rewrite)
1136          $_SERVER['REQUEST_URI'] start with baseURL..
1137          */
1138         if (isset($_SERVER['REQUEST_URI']) && substr($_SERVER['REQUEST_URI'], 0, strlen($this->baseURL)) != $this->baseURL) {
1139             // then we are rewriting?
1140             $this->baseURL = $this->rootURL;
1141             return $request;
1142         }
1143         
1144         //var_dump(array($this->baseURL, $this->rootURL, $request ,$isRedirect, $_SERVER['REQUEST_URI'])); phpinfo();exit;
1145         if (!$request && !$isRedirect) {
1146             
1147             
1148             if ($this->baseURL && (strlen($startRequest) < strlen($this->baseURL))) {
1149                 
1150                 // needs to handle https + port
1151                 $http = ((!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"]  == 'on')) ? 'https' : 'http';
1152                 $sp = '';
1153                 if (!empty($_SERVER['SERVER_PORT'])) {
1154                     if ((($http == 'http') && ($_SERVER['SERVER_PORT'] == 80)) || (($http == 'https') && ($_SERVER['SERVER_PORT'] == 443))) {
1155                         // standard ports..
1156                     } else {
1157                         $sp .= ':'.((int) $_SERVER['SERVER_PORT']);
1158                     }
1159                 }
1160                 //var_dump($startRequest);      echo '<PRE>'; print_R($this);         phpinfo();exit;
1161                 $host = !empty($_SERVER["HTTP_X_FORWARDED_HOST"]) ? $_SERVER["HTTP_X_FORWARDED_HOST"] : $_SERVER["HTTP_HOST"];
1162                 header('Location: '.$http.'://'.$host .$sp . $this->baseURL);
1163  
1164                 exit;
1165             }
1166             $request = "";
1167         }
1168         $this->debug("OUTPUT REQUEST $request<BR>");
1169         return $request;
1170     }
1171     
1172    
1173     
1174     
1175     /**
1176     * get the Class name and filename to load
1177     *
1178     * Parses the request and converts that into a File + Classname
1179     * if the class doesnt exist it will attempt to find a file below it, and
1180     * call that one with the data.
1181     * Used by the module loader to determine the location of the modules
1182     *   
1183     * @param   request  the request string
1184     * @param   boolean showError - if false, allows you to continue if the class doesnt exist.
1185     * 
1186     *
1187     * @return   array classname, filepath
1188     * @access   private
1189     * @static
1190     */
1191   
1192     function requestToClassName($request,$showError=TRUE) 
1193     {
1194        // if ($request == "error") {
1195        //     return array("HTML_FlexyFramework_Error","");
1196        // }
1197         
1198         // special classes ::
1199         if ($this->cli && in_array($request, array('DataObjects'))) {
1200             require_once 'HTML/FlexyFramework2/'. $request . '.php';
1201             return array('HTML_FlexyFramework2_'. $request,'');
1202         }
1203         
1204         
1205         $request_array=explode("/",$request);
1206         $original_request_array = $request_array;
1207         $sub_request_array = array();
1208         $l = count($request_array)-1;
1209         if ($l > 10) { // ?? configurable?
1210             //PEAR::raiseError("Request To Long");
1211             $this->fatalError("Request To Long - " . $request);
1212         }
1213
1214         
1215         $classname='';
1216         // tidy up request array
1217         
1218         if ($request_array) {
1219             foreach(array_keys($request_array) as $i) {
1220                 $request_array[$i] = preg_replace('/[^a-z0-9]/i','_',urldecode($request_array[$i]));
1221             }
1222         }
1223         //echo "<PRE>"; print_r($request_array);
1224         // technically each module should do a check here... similar to this..
1225         
1226         
1227         for ($i=$l;$i >-1;$i--) {
1228             $location = implode('/',$request_array) . ".php";
1229             if ($location == '.php') {
1230                 $this->debug("SKIP first path check, as request str is empty");
1231                 break;
1232             }
1233             
1234             $this->debug("baseDIR = {$this->baseDir}");
1235             
1236             $floc = "{$this->baseDir}/$location";
1237             $this->debug("CHECK LOCATION = $location");
1238             
1239             
1240             
1241             if (!empty($location) && $location != '.php' && @file_exists($floc )) {             // hide? error???
1242                 require_once $floc ;
1243                 $classname = $this->classPrefix . implode('_',$request_array);
1244                 $this->debug("FOUND FILE - SET CLASS = $classname <BR>");
1245                 break;
1246             } 
1247             
1248             // in here check the 'projectExtends' versions..?
1249             
1250             if(!empty($this->projectExtends)){
1251                 $this->debug("Trying project Extends<BR>");
1252                 $has_extend_class = false;
1253                 
1254                 foreach ($this->projectExtends as $e){
1255                     $floc = "{$this->rootDir}/{$e}/$location";
1256                     $this->debug("Trying file: $floc");
1257                     if (!empty($location) && @file_exists($floc)) {             // hide? error???
1258                         require_once $floc ;
1259                         $classname = $e . '_' . implode('_',$request_array);
1260                         $has_extend_class = true;
1261                         $this->debug("FOUND FILE - SET CLASS = $classname <BR>");
1262                         break;
1263                     } 
1264                 }
1265                 
1266                 if(!empty($has_extend_class)){
1267                     break;
1268                 }
1269                 
1270             }
1271             
1272             
1273             $this->debug("$floc  - !!FOUND NOT FILE!!");
1274             
1275             $sub_request_array[] = $original_request_array[$i];
1276             unset($request_array[$i]);
1277             unset($original_request_array[$i]);
1278         }
1279          
1280         // is this really needed here!
1281         
1282         $classname = preg_replace('/[^a-z0-9]/i','_',$classname);
1283         $this->debug("CLASSNAME is '$classname'");
1284         // got it ok.
1285         if ($classname && class_exists($classname)) {
1286             $this->debug("using $classname");
1287             //print_r($sub_request_array);
1288             return array($classname,implode('/',array_reverse($sub_request_array)));
1289         }
1290         // stop looping..
1291         if ($showError) {
1292             $this->fatalError("INVALID REQUEST: \n $request FILE:".$this->baseDir. "/{$location}  CLASS:{$classname}");
1293             
1294         } 
1295         
1296         
1297         $this->debug("Try base {$this->baseDir}.php");   
1298         // try {project name}.php
1299         // this used to be silenced @ - if this fails we are usually pretty fried..
1300         
1301         if (file_exists($this->baseDir.'.php')) {
1302             
1303             
1304             $classname = str_replace('/', '_', $this->project); //   basename($this->baseDir);
1305             
1306             $this->debug("FOUND {$this->baseDir} requring and checking class $classname");   
1307             require_once $this->baseDir.'.php';
1308             $this->debug("require success");
1309             
1310             if (!class_exists($classname)) {
1311                 $this->fatalError( "{$this->baseDir}.php did not contain class $classname");
1312             }
1313         }
1314         // got projectname.php
1315         if ($classname && class_exists($classname)) {
1316             $this->debug("using $classname");
1317             //print_r($sub_request_array);
1318              
1319             return array($classname,implode('/',array_reverse($sub_request_array)));
1320         }    
1321             
1322         
1323         $this->fatalError( "can not find {$this->baseDir}.php"); // dies..
1324               
1325      
1326     }
1327     
1328     /**
1329     * ensure Single CLi process 
1330     * usage:
1331     * HTML_FlexyFramework2::ensureSingle(__FILE__, $this);
1332     * @param string filename of running class
1333     * @param object class
1334     */
1335       
1336     static function ensureSingle($sig, $class) 
1337     {
1338         //echo "check single: $sig / ". get_class($class) ."\n";
1339         $ff = HTML_FlexyFramework2::get();
1340         if (function_exists('posix_getpwuid')) {
1341             $uinfo = posix_getpwuid( posix_getuid () ); 
1342             $user = $uinfo['name'];
1343         } else {
1344             $user = getenv('USERNAME'); // windows.
1345         }
1346         $fdir = ini_get('session.save_path') .'/' . 
1347                 $user . '_cli_' . $ff->project ;
1348      
1349         
1350         if (!file_exists($fdir)) {
1351             mkdir($fdir, 0777);
1352         }
1353         
1354         $lock = $fdir.'/'. md5($sig) . '.' . get_class($class);
1355         //echo "check single: lock : $lock\n";
1356         if (!file_exists($lock)) {
1357             file_put_contents($lock, getmypid());
1358             //echo "check single: lock : DOES NOT EXIST\n";
1359             return true;
1360         }
1361         $oldpid = file_get_contents($lock);
1362         if (!file_exists('/proc/' . $oldpid)) {
1363             
1364             file_put_contents($lock, getmypid());
1365           //  echo "check single: lock : PROC NOT EXIST\n";
1366             return true;
1367         }
1368         // file exists, but process might not be the same..
1369         $name = array_pop(explode('_', get_class($class)));
1370         $cmd = file_get_contents('/proc/' . $oldpid.'/cmdline');
1371         if (!preg_match('/php/i',$cmd) || !preg_match('/'.$name.'/i',$cmd)) {
1372             file_put_contents($lock, getmypid());
1373             //echo "check single: lock : CMDLINE !have PHP \n";
1374             return true;
1375         }
1376         die("process " . $sig . " already running\n");
1377         
1378     }
1379     /**
1380      * removes the lock for the applicaiton - use with care...
1381      *
1382      *
1383      */
1384     static function ensureSingleClear($sig, $class)
1385     {
1386         $ff = HTML_FlexyFramework2::get();
1387         if (function_exists('posix_getpwuid')) {
1388             $uinfo = posix_getpwuid( posix_getuid () ); 
1389             $user = $uinfo['name'];
1390         } else {
1391             $user = getenv('USERNAME'); // windows.
1392         }
1393         $fdir = ini_get('session.save_path') .'/' . 
1394                 $user . '_cli_' . $ff->project ;
1395      
1396         
1397         if (!file_exists($fdir)) {
1398             mkdir($fdir, 0777);
1399         }
1400         $lock = $fdir.'/'. md5($sig);
1401         if (!file_exists($lock)) {
1402             
1403             return true;
1404         }
1405         unlink($lock);;
1406     }
1407     
1408     
1409     /**
1410     * Debugging 
1411     * 
1412     * @param   string  text to output.
1413     * @access   public
1414     */
1415   
1416     function debug($output) {
1417        
1418         if (empty($this->debug)) {  
1419             return;
1420         }
1421         echo $this->cli ? 
1422               "HTML_FlexyFramework2::debug  - ".$output."\n" 
1423             : "<B>HTML_FlexyFramework2::debug</B> - ".$output."<BR>\n";
1424     
1425     }
1426     /**
1427     * Raises a fatal error. - normally only used when setting up to help get the config right.
1428     * 
1429     * can redirect to fatal Action page.. - hoepfully not issued before basic vars are set up..
1430     * 
1431     * @param   string  text to output.
1432     * @access   public
1433     */
1434     
1435     function fatalError($msg,$showConfig = 0) 
1436     {
1437         
1438         
1439          if ($this->fatalAction) {
1440             HTML_FlexyFramework2::run($this->fatalAction,$msg);
1441             exit;
1442         }
1443         header('HTTP/1.1 503 Service Temporarily Unavailable');
1444         header('Status: 503 Service Temporarily Unavailable');
1445         header('Retry-After: 300');
1446         echo $this->cli ? $msg ."\n" : "<H1>$msg</H1>configuration information<PRE>";
1447         if ($showConfig) {
1448             
1449             print_r($this);
1450         }
1451         $ff = HTML_FlexyFramework2::get();
1452         $ff->debug($msg);
1453         exit;
1454     }    
1455 }
1456