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