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