root / phpframework / trunk / fuseboxApplication.php

Revision 635, 55.3 kB (checked in by starkraving2002, 1 year ago)
  • Property svn:executable set to *
Line 
1<?php
2/*
3Fusebox Software License
4Version 1.0
5
6Copyright (c) 2003, 2004, 2005, 2006 The Fusebox Corporation. All rights reserved.
7
8Redistribution and use in source and binary forms, with or without modification, are permitted
9provided that the following conditions are met:
10
111. Redistributions of source code must retain the above copyright notice, this list of conditions
12   and the following disclaimer.
13
142. Redistributions in binary form or otherwise encrypted form must reproduce the above copyright
15   notice, this list of conditions and the following disclaimer in the documentation and/or other
16   materials provided with the distribution.
17
183. The end-user documentation included with the redistribution, if any, must include the following
19   acknowledgment:
20
21   "This product includes software developed by the Fusebox Corporation (http://www.fusebox.org/)."
22
23   Alternately, this acknowledgment may appear in the software itself, if and wherever such
24   third-party acknowledgments normally appear.
25
264. The names "Fusebox" and "Fusebox Corporation" must not be used to endorse or promote products
27   derived from this software without prior written (non-electronic) permission. For written
28   permission, please contact fusebox@fusebox.org.
29
305. Products derived from this software may not be called "Fusebox", nor may "Fusebox" appear in
31   their name, without prior written (non-electronic) permission of the Fusebox Corporation. For
32   written permission, please contact fusebox@fusebox.org.
33
34If one or more of the above conditions are violated, then this license is immediately revoked and
35can be re-instated only upon prior written authorization of the Fusebox Corporation.
36
37THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
38LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39DISCLAIMED. IN NO EVENT SHALL THE FUSEBOX CORPORATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY
40DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
42BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
43STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
44OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45
46-------------------------------------------------------------------------------
47
48This software consists of voluntary contributions made by many individuals on behalf of the
49Fusebox Corporation. For more information on Fusebox, please see <http://www.fusebox.org/>.
50
51*/
52class FuseboxApplication { //I am the Fusebox application object, formerly the application.fusebox data structure
53   
54    var $myVersion;
55    var $factory;
56    var $fuseboxLexicon;
57    var $customAttributes;
58    var $fuseboxVersion;
59    var $appKey;
60    var $coreRoot;
61   
62    function FuseboxApplication() {
63        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
64        // initialize the fusebox (available to be read by developers but not to be written to)
65        $this->isFullyLoaded = false;
66        $this->circuits = array();
67        $this->classes = array();
68        $this->lexicons = array();
69        $this->plugins = array();
70        $this->pluginPhases = array();
71        $this->nonFatalExceptionPrefix = "INFORMATION (can be ignored): ";
72   
73        $this->precedenceFormOrURL = "form";
74        $this->defaultFuseaction = "";
75        $this->fuseactionVariable = "fuseaction";
76        // this is ignored:
77        $this->parseWithComments = false;
78        $this->ignoreBadGrammar = true;
79        $this->allowLexicon = true;
80        $this->useAssertions = true;
81        $this->conditionalParse = false;
82       
83        $this->password = "";
84        $this->mode = "production";
85        $this->scriptLanguage = "php4";
86        $this->scriptFileDelimiter = "php";
87        $this->scriptVersion = phpversion();
88        $this->maskedFileDelimiters = "htm,cfm,cfml,php,php4,asp,aspx";
89        $this->characterEncoding = "utf-8";
90        // this is ignored:
91        $this->parseWithIndentation = $this->parseWithComments;
92        $this->strictMode = false;
93        $this->allowImplicitCircuits = false;
94        $this->debug = false;
95        $this->hasProcess = array('appinit'=>false,'preprocess'=>false,'postprocess'=>false);
96        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '</li><li>Ending $'.__CLASS__.'->'.__FUNCTION__.'()</li></ul>';
97    }
98   
99    function &init /*I am the constructor */ (
100            $appKey, //I am FUSEBOX_APPLICATION_KEY
101            $appPath, //I am FUSEBOX_APPLICATION_PATH
102            $appName, //I am FUSEBOX_APPLICATION_Name
103            &$myFusebox //I am the myFusebox data structure
104        ) {
105        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
106        $this->myVersion = "5.0.0.GR.0";
107        require_once("fuseboxFactory.php");
108        if ( !isPHP5() ) { eval('$this->factory =& new FuseboxFactory();'); } else { $this->factory = new FuseboxFactory(); }
109       
110        $this->fuseboxLexicon = $this->factory->getBuiltinLexicon();
111        $this->customAttributes = array();
112       
113        $this->fuseboxVersion = $this->myVersion;
114       
115        $this->appKey = $appKey;
116        $this->appName = $appName;
117        $this->webrootdirectory = str_replace("\\","/",getcwd().DIRECTORY_SEPARATOR);
118        $this->coreRoot = str_replace("\\","/",dirname(__FILE__).DIRECTORY_SEPARATOR);
119
120        $this->approotdirectory = str_replace("\\","/",$this->webrootdirectory) . str_replace("\\","/",$appPath);
121        if ( substr($this->approotdirectory,-1) != "/" ) {
122            $this->approotdirectory .= "/";
123        }
124        // remove pairs of directory/../ to form canonical path:
125        while ( strpos($this->approotdirectory,'/../') !== false ) {
126            $this->approotdirectory = ereg_replace("[^\.:/]*/\.\./","",$this->approotdirectory);
127        }
128        // this works on all platforms:
129        $this->osdelimiter = "/";
130
131        $this->coreToAppRootPath = $this->relativePath($this->coreRoot,$this->approotdirectory);
132        $this->appRootPathToCore = $this->relativePath($this->approotdirectory,$this->coreRoot);
133        $this->coreToWebRootPath = $this->relativePath($this->coreRoot,$this->webrootdirectory);
134        $this->WebRootPathToCore = $this->relativePath($this->webrootdirectory,$this->coreRoot);
135        $this->WebRootPathToappRoot = $this->relativePath($this->webrootdirectory,$this->approotdirectory);
136       
137        $this->parsePath = "parsed/";
138        $this->parseRootPath = "../";
139        $this->pluginsPath = "plugins/";
140        $this->lexiconPath = "lexicon/";
141        $this->errortemplatesPath = "errortemplates/";
142       
143        $this->circuits = array();
144        $this->reload($appKey,$appPath,$myFusebox);
145
146        if ( $this->strictMode ) {
147            // rootdirectory was deprecated in Fusebox 5 so we no longer set it it strict mode:
148            $this->rootdirectory = null;
149        } else {
150            // for FB4.0 compatibility:
151            $this->rootdirectory = $this->approotdirectory;
152        }
153       
154        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '</li><li>Ending $'.__CLASS__.'->'.__FUNCTION__.'()</li></ul>';
155        return $this;
156
157    }
158
159    function reload /*I (re)load the fusebox.xml file into memory and (re)load all of the application components referenced by that.*/ (
160            $appKey, //I am FUSEBOX_APPLICATION_KEY
161            $appPath, //I am FUSEBOX_APPLICATION_PATH.
162            &$myFusebox //I am the myFusebox data structure.
163        ) {
164        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
165       
166        $fbFile = "fusebox.xml.php";
167        $fbXML = "";
168        $fbCode = "";
169        $encodings = 0;
170        $needToLoad = true;
171        $fuseboxFiles = 0;
172
173        if ( isset($this->timestamp) && ( false !== ( $fuseboxFiles = opendir($this->approotdirectory) ) ) ) {
174            $found = false;
175            while ( false !== ( $file = readdir($fuseboxFiles) ) && !$found ) {
176                if ( strpos('fusebox.xml',$file) === 0 ) {
177                    $needToLoad = ( filemtime($this->approotdirectory.$file) > $this->timestamp );
178                    $found = true;
179                }
180            // else ignore the ambiguity
181            }
182        }
183
184        // FB5: fusebox.loadclean will delete all the parsed files
185        if ( $myFusebox->parameters['clean'] ) {
186            $this->deleteParsedFiles();
187        }
188       
189        require_once("udf_XMLUtils.php");
190        if ( $needToLoad ) {
191            if ( $this->debug ) {
192                $myFusebox->trace("Compiler","Loading fusebox.xml file");
193            }
194            // attempt to load fusebox.xml(.php):
195            if ( !file_Exists($this->approotdirectory . $fbFile) ) {
196                $fbFile = "fusebox.xml";
197            }
198           
199            do {
200                $okay = false;
201                if ( false == ( $fbXMLfile = @fopen($this->approotdirectory . $fbFile,"r") ) ) break;
202                if ( false == ( $fbXML = @fread($fbXMLfile,filesize($this->approotdirectory . $fbFile)) ) ) break;
203                if ( false == ( @fclose($fbXMLfile) ) ) break;
204                $okay = true;
205            } while ( false );
206            if ( !$okay ) {
207                __cfthrow(array( 'type'=>"fusebox.missingFuseboxXML",
208                    'message'=>"missing fusebox.xml",
209                    'detail'=>"The file '".$fbFile."' could not be found in $this->approotdirectory."
210                ));
211            }
212           
213            do {
214                $okay = false;
215                // see if we need to re-read based on the encoding being different to our default
216                if ( ereg('(<parameters>).+(<parameter name="characterEncoding" value=")([^"]+)',$fbXML,$match) ) {
217                    $this->encodings trim($match[3]);
218                } else {
219                    $this->encodings = "utf-8";
220                }
221                if ( false == ($fbCode = @xmlParse($fbXML,$this->encodings) ) ) break;
222                $okay = true;
223            } while ( false );
224            if ( !$okay ) {
225                __cfthrow(array( 'type'=>"fusebox.fuseboxXMLError",
226                    'message'=>"Error reading fusebox.xml",
227                    'detail'=>"A problem was encountered while reading the ".$fbFile." file. This is usually caused by unmatched XML tags (a &lt;tag&gt; without a &lt;/tag&gt; or without use of the &lt;tag/&gt; short-cut.)"
228                ));
229            }
230           
231            $this->loadParameters($fbCode);
232            $this->loadLexicons($fbCode);
233            $this->loadClasses($fbCode);
234            $this->loadPlugins($fbCode);
235            $this->loadGlobalPreAndPostProcess($fbCode);
236            // save fusebox.xml DOM internally for (re-)loading circuits
237            $this->cacheFBCode($fbCode,$myFusebox);
238            $this->fbFile = $fbFile;
239        } else {
240            $fbCode = $this->getFBCode();
241        }
242       
243        // to track circuit loads on this request
244        if ( !isset($_REQUEST['__fusebox']['CircuitsLoaded']) ) $_REQUEST['__fusebox']['CircuitsLoaded'] = array();       
245        $this->loadCircuits($fbCode,$myFusebox);
246       
247       
248        // application data available to developers via getApplicationData() method:
249        $this->data = array();
250       
251        $this->isFullyLoaded = true;
252        $this->applicationStarted = false;
253        $this->timestamp = microtime();
254        $this->timestamp = (substr($this->timestamp,-strpos($this->timestamp,' '))) * 1000;
255        $this->dateLastLoaded = microtime();
256        $this->dateLastLoaded = (substr($this->dateLastLoaded,-strpos($this->dateLastLoaded,' '))) * 1000;
257       
258        /*
259            The following documented parts of application.fusebox are not supported in Fusebox 5:
260            - application.fusebox.xml
261            - application.fusebox.globalfuseactions.*
262            - application.fusebox.circuits.*.xml
263            - application.fusebox.circuits.preFuseaction.*
264            - application.fusebox.circuits.postFuseaction.*
265            - application.fusebox.circuits.*.fuseactions.*.xml
266        */
267       
268        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '</li><li>Ending $'.__CLASS__.'->'.__FUNCTION__.'()</li></ul>';
269    }
270   
271    function getPluginsPath() { //I am a convenience method to return the location of the plugins.
272        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
273   
274        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '</li><li>Ending $'.__CLASS__.'->'.__FUNCTION__.'()</li></ul>';
275        return $this->pluginsPath;
276   
277    }
278   
279    function getApplicationData() { //I return a reference to the application data cache. This is a new concept in Fusebox 5.
280        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
281   
282        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '</li><li>Ending $'.__CLASS__.'->'.__FUNCTION__.'()</li></ul>';
283        return $this->data;
284   
285    }
286   
287    function getApplicationRoot() { //I am a convenience method to return the full application root directory path.
288        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
289   
290        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '</li><li>Ending $'.__CLASS__.'->'.__FUNCTION__.'()</li></ul>';
291        return $this->approotdirectory;
292   
293    }
294   
295    function getFuseboxXMLFilename() { //I return the actual name of the fusebox.xml(.cfm) file.
296        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
297   
298        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '</li><li>Ending $'.__CLASS__.'->'.__FUNCTION__.'()</li></ul>';
299        return $this->fbFile;
300   
301    }
302   
303    function getCoreToAppRootPath() { //I am a convenience method to return the relative path from the core files to the application root.
304        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
305   
306        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '</li><li>Ending $'.__CLASS__.'->'.__FUNCTION__.'()</li></ul>';
307        return $this->coreToAppRootPath;
308   
309    }
310   
311    function getWebRootPathToappRoot() { // I am a convenience method to return the relative path from the webroot to the application root.
312        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
313        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '</li><li>Ending $'.__CLASS__.'->'.__FUNCTION__.'()</li></ul>';
314        return $this->WebRootPathToappRoot;
315    }
316   
317    function compileAll /*I compile all the public fuseactions in the application. */ (
318            &$myFusebox //I am the myFusebox data structure.
319        ) {
320        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
321
322        $c = 0;
323        $a = 0;
324        $f = 0;
325   
326        foreach ( array_keys($this->circuits) as $c ) {
327            $cir =& $this->circuits[$c];
328            $a =& $cir->getFuseactions();
329            foreach ( array_keys($a) as $f ) {
330                if ( $a[$f]->access == "public" ) {
331                    $this->compileRequest($c . "." . $f,$myFusebox);
332                }
333            }
334        }
335        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '</li><li>Ending $'.__CLASS__.'->'.__FUNCTION__.'()</li></ul>';
336
337    }
338   
339    function compileRequest /*I compile a specific (public) fuseaction as an external request.*/ (
340            $circuitFuseaction, //I am the full name of the requested fuseaction (circuit.fuseaction).
341            &$myFusebox //I am the myFusebox data structure.
342        ) {
343        if ( isset($GLOBALS['attributes']['fusebox.debug']) && $GLOBALS['attributes']['fusebox.debug'] == 'true' ) echo '<ul><li>Starting $'.__CLASS__.'->'.__FUNCTION__.'()';
344        $myVersion = $this->getVersion();
345        list($circuit,$fuseaction) = explode('.',$circuitFuseaction);
346        $i = 0;
347        $n = 0;
348        $needRethrow = true;
349        $needTryOnFuseaction = false;
350        $parsedName = strtolower($circuitFuseaction).".php";
351        $parsedFile = $this->getWebRootPathToappRoot().$this->parsePath.$parsedName;
352        $fullParsedFile = $this->getApplicationRoot().$this->parsePath.$parsedName;
353        $result = array();
354        $writer = 0;
355       
356        // validate format of the fuseaction:
357        if ( count(explode(".",$circuitFuseaction)) != 2 || substr($circuitFuseaction,-1) == '.' ) {
358            __cfthrow(array( 'type'=>"fusebox.malformedFuseaction",
359                'message'=>"malformed Fuseaction",
360                'detail'=>"You specified a malformed Fuseaction of ".$circuitFuseaction.". A fully qualified Fuseaction must be in the form [Circuit].[Fuseaction]."
361            ));
362        }
363       
364        // to track reloads on this request
365        if ( !isset($_REQUEST['__fusebox']['CircuitsLoaded']) ) $_REQUEST['__fusebox']['CircuitsLoaded'] = array();
366        if ( !isset($_REQUEST['__fusebox']['fuseactionsDone']) ) $_REQUEST['__fusebox']['fuseactionsDone'] = array();
367       
368        // set up myFusebox values for this request:
369        $myFusebox->originalCircuit = $circuit;
370        $myFusebox->originalFuseaction = $fuseaction;
371        foreach ( array_keys($this->plugins) as $i ) {
372            $myFusebox->plugins[$i] = array();
373        }
374
375        // note that in Fusebox 5, these are really all the same set of files
376        $myFusebox->version['loader'] = $myVersion;
377        $myFusebox->version['parser'] = $myVersion;
378        $myFusebox->version['transformer'] = $myVersion;
379        // legacy test from FB41 although it's a bit pointless
380        if ( $myFusebox->version['runtime'] != $myFusebox->version['loader'] ) {
381            __cfthrow(array( 'type'=>"fusebox.versionMismatchException",
382                'message'=>"The loader is not the same version as the runtime",
383                'detail'=>""
384            ));
385        }
386       
387        // check access on request - if the circuit/fuseaction doesn't exist we trap it later
388        if ( array_key_exists($circuit,$this->circuits) &&
389                array_key_exists($fuseaction,$this->circuits[$circuit]->fuseactions) &&
390                $this->circuits[$circuit]->fuseactions[$fuseaction]->getAccess() != "public" ) {
391            __cfthrow(array( 'type'=>"fusebox.invalidAccessModifier",
392                'message'=>"Invalid Access Modifier",
393                'detail'=>"You tried to access $circuit.$fuseaction which does not have access modifier of public. A Fuseaction which is to be accessed from anywhere outside the application (such as called via an URL, or a FORM, or as a web service) must have an access modifier of public or if unspecified at least inherit such a modifier from its circuit."
394            ));
395        }
396        if ( !file_Exists($fullParsedFile) || $myFusebox->parameters['parse'] ) {
397            $fp = @fopen($fullParsedFile,"r");
398            //if ( !flock($fp,LOCK_EX) ) die('Could not get exclusive lock to parsed fuseaction file');
399                if ( !file_Exists($fullParsedFile) || $myFusebox->parameters['parse'] ) {
400                    $_REQUEST['__fusebox']['SuppressPlugins'] = false;
401                    require_once("fuseboxWriter.php");
402                    if ( !isPHP5() ) { eval('$writer =& new FuseboxWriter($this,$myFusebox);'); } else { $writer = new FuseboxWriter($this,$myFusebox); }
403                    $writer->open($parsedName);
404                    $writer->rawPrintln("// circuit: $circuit");
405                    $writer->rawPrintln("// fuseaction: $fuseaction");
406                    if ( array_key_exists("processError",$this->pluginPhases) &&
407                            !$_REQUEST['__fusebox']['SuppressPlugins'] ) {
408                        if ( $this->scriptVersion{0} == '5' ) {
409                            $writer->rawPrintln("try {");