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