Page title
+H2O template inheritance is a powerful tool
+ {% endblock %} +diff --git a/.htaccess b/.htaccess index 6571402..8252d42 100644 --- a/.htaccess +++ b/.htaccess @@ -1,2 +1,5 @@ RewriteEngine on -RewriteRule ^(.*)$ /index.php/$1 [L] \ No newline at end of file +RewriteBase /test +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^(.*)$ index.php/$1 [L] \ No newline at end of file diff --git a/application/.htaccess b/application/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/application/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/application/config.php b/application/config.php new file mode 100644 index 0000000..a7569ee --- /dev/null +++ b/application/config.php @@ -0,0 +1,5 @@ +run(); \ No newline at end of file diff --git a/system/.htaccess b/system/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/system/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/system/engine/.htaccess b/system/engine/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/system/engine/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/system/engine/SMTP.php b/system/engine/SMTP.php new file mode 100644 index 0000000..7a7a5df --- /dev/null +++ b/system/engine/SMTP.php @@ -0,0 +1,80 @@ +from = $from; + $this->to = $to; + $this->subject = $subject; + $this->msg = $msg; + $this->user = $user; + $this->password = $password; + $this->port = $port; + $this->server = $server; + } + + public function send($html=false) + { + $err = null; + $errstr = ""; + $this->smtpserverconn = fsockopen($this->server, $this->port, $err, $errstr, 100); + $response = fgets($this->smtpserverconn, 515); + if ($response === false) + { + throw new Exception("Could not connect to SMTP server!"); + } + + if ($this->user != null && $this->password != null) + { + $this->sendCommand("AUTH LOGIN"); + $this->sendCommand(base64_encode($this->user)); + $this->sendCommand(base64_encode($this->password)); + } + + $this->sendCommand("HELO " . $_SERVER["SERVER_NAME"]); + $this->sendCommand("MAIL FROM: " . $this->from); + $this->sendCommand("RCPT TO: " . $this->to); + $this->sendCommand("DATA"); + + if ($html) + { + $this->sendCommand("MIME-Version: 1.0", false); + $this->sendCommand("Content-type: text/html; charset=iso-8859-1", false); + } + + $this->sendCommand("From: " . $this->from, false); + $this->sendCommand("To: " . $this->to, false); + $this->sendCommand("Subject: " . $this->subject, false); + + + $this->sendCommand($this->msg, false); + + $this->sendCommand("", false); + $this->sendCommand(".", false); + $this->sendCommand("QUIT"); + + } + + private function sendCommand($command, $return = true) + { + fputs($this->smtpserverconn, $command . "\r\n"); + if ($return) + return fgets($this->smtpserverconn, 515); + else + return null; + } +} \ No newline at end of file diff --git a/system/engine/config-default.php b/system/engine/config-default.php new file mode 100644 index 0000000..d2bd78e --- /dev/null +++ b/system/engine/config-default.php @@ -0,0 +1,14 @@ +config = $config; + $this->tpl = $tpl; + $this->core = $core; + } + +} \ No newline at end of file diff --git a/system/engine/core.php b/system/engine/core.php index 23a1a66..7171cf0 100644 --- a/system/engine/core.php +++ b/system/engine/core.php @@ -1,63 +1,235 @@ findController(); + $config = include("system/engine/config-default.php"); + if (is_file("application/config.php")) + { + $newconfig = include("application/config.php"); + } + $this->config = array_merge($config, $newconfig); + if ($this->config["USE_H20_TPL"]) + $this->tpl = new H2o(null, array( + "searchpath" => getcwd() . "/application/views/", "cache_dir" => "application/tmp/", + )); + set_error_handler("HF_Core::error_handler"); + $this->findController(); } private function findController() { - $request = $_SERVER["REQUEST_URI"]; - if ($request == "" || $request == "/") - { - require("../../application/controllers/main.php"); - $this->$class = new main(); - $this->$method = "index"; - $this->$classname = "main"; - return; - } - $arr = explode("/", $request); - $path = "../../application/controllers/"; - for($i = 0; $i < count($arr); $i++) - { - if ($is_file($path . $arr[$i] . ".php")) // found the controller - { - include($path . $arr[$i] . ".php"); - $this->$class = new $arr[$i]; - - if ($i + 1 != count($arr)) // if there is a define after the controller name - this would be the method name - { - $this->$method = $arr[$i+1]; - $this->$args = array_slice ($arr, 2); - $this->$classname = $arr[$i]; - } else { // call index - $this->$method = "index"; - $this->$classname = $arr[$i]; - } - return; - } - - if (is_dir($path . $arr[$i])) // controller is hidden deeper - { - $path = $path . $arr[$i] . "/"; - continue; - } - - // throw exception controller not found - } - + try + { + $request = $_SERVER["PHP_SELF"]; + $splitreq = explode("/", $request); + $request = ""; + for($i = 0; $i < count($splitreq); $i++) + { + if ($splitreq[$i] == "index.php") + { + $request = implode("/", array_splice($splitreq, $i+1)); + } + } + + if ($request == "" || $request == "/") + { + require("application/controllers/" . $this->config["DEFAULT_ROUTE"] . ".php"); + $this->loadController(new $this->config["DEFAULT_ROUTE"]($this->config, $this, $this->tpl), $this->config["DEFAULT_ROUTE"], "index"); + return; + } + if ($request[strlen($request)-1] == "/") + $request = substr($request, 0, -1); + $arr = explode("/", $request); + $path = "application/controllers/"; + for($i = 0; $i < count($arr); $i++) + { + if (is_file($path . $arr[$i] . ".php")) // found the controller + { + include($path . $arr[$i] . ".php"); + if ($i + 1 < count($arr)) // if there is a define after the controller name - this would be the method name + { + $this->loadController(new $arr[$i]($this->config, $this, $this->tpl), $arr[$i], $arr[$i+1], array_slice ($arr, 2)); + } else { // call index + $this->loadController(new $arr[$i]($this->config, $this, $this->tpl), $arr[$i], "index"); + } + return; + } + + if (is_dir($path . $arr[$i])) // controller is hidden deeper + { + $path = $path . $arr[$i] . "/"; + continue; + } + + $this->load404Controller(); + break; + // throw exception controller not found + } + } catch (Exception $e) { + if ($this->config["DEBUG"]) + echo vdump($e, $this); + else + $this->mail_admins("[Exception - " . $this->config["SITE_NAME"] . "]", vdump($e, $this), true); + } } + + private function load404Controller() + { + if (is_file(getcwd() . "/application/status.php")) + { + include_once (getcwd() . "/application/status.php"); + $this->loadController(new status($this->config, $this, $this->tpl), "status", "Status404"); + } else { + include_once(getcwd() . "/system/engine/status.php"); + $this->loadController(new HF_Status($this->config, $this, $this->tpl), "HF_Status", "Status404"); + + } + } + + private function load500Controller() + { + if (is_file(getcwd() . "/application/status.php")) + { + include_once (getcwd() . "/application/status.php"); + $this->loadController(new status($this->config, $this, $this->tpl), "status", "Status500"); + } else { + include_once (getcwd() . "/system/engine/status.php"); + $this->loadController(new HF_Status($this->config, $this, $this->tpl), "HF_Status", "Status500"); + + } + } + + private function loadController($class, $classname, $method, $args = array()) + { + $this->class = $class; + $this->classname = $classname; + $this->method = $method; + $this->args = $args; + } - public function run() + public function run($err=false) { - $call = new ReflectionFunction($this->$classname, $this->$method); - $call->invokeArgs($this->$class, $this->$args); + try + { + $call = new ReflectionMethod($this->classname, $this->method); + if ($err) + { + $call->invokeArgs($this->class, $this->args); + return; + } + + $numOfReqPara = $call->getNumberOfRequiredParameters(); + $numOfOptPara = $call->getNumberOfParameters() - $numOfReqPara; + $remainparas = count($this->args) - $numOfReqPara; + if ($remainparas >= 0 && $remainparas <= $numOfOptPara) + { + $call->invokeArgs($this->class, $this->args); + } + else + { + $this->load404Controller(); + $this->run(true); + } + + } catch (ReflectionException $e) + { + if (strstr($e->getMessage(), "does not exist") !== false) + { + $this->load404Controller(); + } else { + $this->load500Controller(); + } + $this->run(true); + if ($this->config["DEBUG"]) + echo vdump($e, $this); + else + $this->mail_admins("[Exception - " . $this->config["SITE_NAME"] . "]", vdump($e, $this), true); + + + } catch (Exception $e) { + $this->load500Controller(); + $this->run(true); + if ($this->config["DEBUG"]) + echo vdump($e, $this); + else + $this->mail_admins("[Exception - " . $this->config["SITE_NAME"] . "]", vdump($e, $this), true); + } } + + public function mail_admins($subject, $msg, $html = false) + { + if (array_key_exists("ADMINS", $this->config)) + { + foreach($this->config["ADMINS"] as $email) + { + $this->mail_user($email, $subject, $msg, $html); + } + } + } + + public function mail_user($to, $subject, $msg, $html = false) + { + if ($this->config["USE_HF_SMTP"]) + { + $smtp = new HF_SMTP($this->config["SMTP_FROM"], $to, $subject, $msg, $this->config["SMTP_SERVER"], $this->config["SMTP_USER"], $this->config["SMTP_PASS"], $this->config["SMTP_PORT"]); + $smtp->send($html); + } else { + require_once "Mail.php"; + $smtp = null; + if ($this->$this->config["SMTP_USER"] && $this->config["SMTP_PASS"]) + $smtp = Mail::factory('smtp', array( + "host" => $this->config["SMTP_SERVER"], + "port" => $this->config["SMTP_PORT"], + "auth" => true, + 'username' => $this->config["SMTP_USER"], + 'password' => $this->config["SMTP_PASS"] + )); + else + $smtp = Mail::factory('smtp', array( + "host" => $this->config["SMTP_SERVER"], + "port" => $this->config["SMTP_PORT"] + )); + $headers = array ('From' => $this->config["SMTP_FROM"], + 'To' => $to, + 'Subject' => $subject); + $smtp->send($to, $headers, $msg); + } + } + + public static function error_handler($err_severity, $err_msg, $err_file, $err_line, array $err_context) + { + if (0 === error_reporting()) { return false;} + switch($err_severity) + { + case E_ERROR: throw new ErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_WARNING: throw new WarningException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_PARSE: throw new ParseException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_NOTICE: throw new NoticeException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_CORE_ERROR: throw new CoreErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_CORE_WARNING: throw new CoreWarningException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_COMPILE_ERROR: throw new CompileErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_COMPILE_WARNING: throw new CoreWarningException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_USER_ERROR: throw new UserErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_USER_WARNING: throw new UserWarningException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_USER_NOTICE: throw new UserNoticeException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_STRICT: throw new StrictException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_RECOVERABLE_ERROR: throw new RecoverableErrorException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_DEPRECATED: throw new DeprecatedException ($err_msg, 0, $err_severity, $err_file, $err_line); + case E_USER_DEPRECATED: throw new UserDeprecatedException ($err_msg, 0, $err_severity, $err_file, $err_line); + } + + } } \ No newline at end of file diff --git a/system/engine/exceptions.php b/system/engine/exceptions.php new file mode 100644 index 0000000..69b0076 --- /dev/null +++ b/system/engine/exceptions.php @@ -0,0 +1,18 @@ +](http://code.google.com/p/h2o-template/downloads) + +With Git + +`git clone http://github.com/speedmax/h2o-php.git` + +With SVN + +`svn checkout http://h2o-template.googlecode.com/svn/trunk/ h2o` + +### Installation + 1. Download and extract h2o into your project path or your php include path + + Sample file structure setup + + myawesome_app/ + index.php + templates/ + index.html + h2o/ + + + 2. Use `require 'h2o/h2o.php'` in your php files to include the h2o library. + 3. Below is a basic code sample to get your project going. + 3. Check out the *\example* and *\specs* dirs to see some of h2o's more interesting features in action. + +*templates/index.html* + +
+H2O template inheritance is a powerful tool
+ {% endblock %} +Body of extended page
+ {% endblock %} + + {% block sidebar %} + Sidebar contains use links. + {% endblock %} + + +The `page.html` extends `base.html`, allowing us to override any block +previously defined in `base.html`. + +Below is an excellent article about template inheritance in Django. If you wish to understand H2o's +template-inheritance system, this would be a great spot to start, since H2o's template-inheritance system +is strongly influenced by Django's. + +[Power of inheritance][3] is a very good blog post explaining inheritance + + [3]:http://www2.jeffcroft.com/blog/2006/feb/25/django-templates-the-power-of-inheritance/ + +*Tips* + +* If you have found that you have several common elements inside the same template, it may be a + good idea to put that portion of the template inside a `block` in a base template. +* `block` give you a hook, which is useful, since these can help with javascript and css too. +* When defining a block use a short and distinctive name + + + +### Configuration +There are a range of options for configuring the template engine. + + [option_value] + )); + ?> + +#### Loader +The name of the loader or an instance of H2o_Loader + +__Use file loader [default]__ + +` $template = new H2o('index.html', array('loader'=>'file'); ` + + +__Advanced setup__ + $loader ); + ?> + +__Use dictionary loader__ + +If you want to load templates from resources other than files, then this will be your +friend. H2o uses `dict_loader()` for testing. + + 'Hello {{ person }}' + )); + $template = new H2o('index.html', array('loader' => $loader')); + ?> + +#### Searchpath + +default: this will be the base path of your template + +H2o use this path to load additional templates and extensions. + +You can either explicity set the search path, + +`$template = new H2o('index.html', array('searchpath' => '/sites/common_templates'));` + +or h2o will try to find the searchpath for you. + +`$template = new H2o('/sites/common_templates/index.html');` + +#### Cache +You can define the type of caching engine h2o should use, if any. +Set 'cache' to false to disable caching. +You can read more about performance and caching in following sections + +Use file cache [default] + +`$template = new H2o('index.html', array('cache'=>'file'));` + +Use apc cache: + +`$template = new H2o('index.html', array('cache'=>'apc'));` + +Use memcache cache + +`$template = new H2o('index.html', array('cache'=>'memcache'));` + +Disable caching + +`$template = new H2o('index.html', array('cache'=>false));` + +#### Cache_dir +When the file cache is used, you can define where you want templates to be cached. + +It will put a cached template in same location as the normal template + +`$template = new H2o('index.html', array('cache_dir'=>'/tmp/cached_templates'));` + +#### Cache_ttl +"cache_ttl" specifies how long a cached template should be used (defaults: 1 hour) before it is recompiled. The template fragment cache +that is bundled in the cache extension will use this as default ttl value. + +`$template = new H2o('index.html', array('cache_ttl' => 3600));` + + +Performance and Caching +------------------------ + +Caching can increase performance since it skips step of inefficient template parsing. +H2o caches the template objects (the internal data structure of a template) and the bundled +caching backends include File, APC, and Memcache. + +### File cache +By default h2o uses the file cache to store template objects. Change h2o option `cache_dir` to where you +want to store template cache (ie: /tmp). + + 'file', + 'cache_dir' => '/tmp' + )); + ?> + +### APC cache +APC is an op-code and object cache extension for php whose performance is +generally 10-30% better than just plain file caching. + + 'apc')); + ?> + +### Memcache +Currently not implemented + + +Extending H2o +------------------------ + + +Known issues +------------------------ +Yes, h2o has them. However, if you are careful, these shouldn't hinder your template development. +The deep inheritance issue is a bit problematic for some template architectures, but again, if you +are careful, and perhaps a bit inventive, it won't hinder you terribly much. + + * `{{ block.super }}` doesn't work with more than 1 level of inheritance yet, so if `{{ block.super }}` + invokes another `{{ block.super }}` it won't work just yet. + * 'if' conditions don't support multiple expressions or mathematical expressions yet, like: + `{% if something > 3 or something < 2 %}` or `{% if something + else > 12 %}` + These likely will not be implemented in the future unless some daring soul implements them and + contributes the code back to the h2o-php project. + + +Contributors +--- + - Taylor Luk - Founder of [Issue publishing](http://issueapp.com) + - jlogsdon - Major refactoring (wip) and bug fixes + - cyberklin - Added filter support for any context resolve + - idlesign - Added if_changed tag support + - metropolis - Improved our test coverage + - plus many others + + +Credit +------------------------ +H2o borrows ideas and/or concepts from the following projects: + + - Django template - Django web development framework. + - Ptemplates - Armin Ronacher's pet project for a django port in PHP. + - Jinja - Django inspired template in Python. + +Special Thanks: Armin Ronacher, since early versions of h2o were based off of his Ptemplates project. + +The MIT License +------------------------ +Copyright (c) 2008 Taylor Luk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/system/vendor/StackTracePrint.php b/system/vendor/StackTracePrint.php new file mode 100644 index 0000000..df4506d --- /dev/null +++ b/system/vendor/StackTracePrint.php @@ -0,0 +1,66 @@ +"; + + $ret .- "".htmlspecialchars(trim($code[$backtrace[0]['line']-1]))."\n"; + + $ret .= "\n"; + + ob_start(); + + foreach ($args as $arg) + var_dump($arg); + + $str = ob_get_contents(); + + ob_end_clean(); + + $str = preg_replace('/=>(\s+)/', ' => ', $str); + $str = preg_replace('/ => NULL/', ' → NULL', $str); + $str = preg_replace('/}\n(\s+)\[/', "}\n\n".'$1[', $str); + $str = preg_replace('/ (float|int)\((\-?[\d\.]+)\)/', " $1 $2", $str); + + $str = preg_replace('/array\((\d+)\) {\s+}\n/', "array•$1 []", $str); + $str = preg_replace('/ string\((\d+)\) \"(.*)\"/', " str•$1 '$2'", $str); + $str = preg_replace('/\[\"(.+)\"\] => /', "'$1' → ", $str); + $str = preg_replace('/object\((\S+)\)#(\d+) \((\d+)\) {/', "obj•$2 $1[$3] {", $str); + $str = str_replace("bool(false)", "bool•false", $str); + $str = str_replace("bool(true)", "bool•true", $str); + + $ret .= $str; + + + + $ret .= ""; + + return $ret; + + + +} + +// Original - http://www.php.net/manual/en/function.debug-print-backtrace.php#86932 +function debug_string_backtrace() { + ob_start(); + debug_print_backtrace(); + $trace = ob_get_contents(); + ob_end_clean(); + + // Remove first item from backtrace as it's this function which + // is redundant. + $trace = preg_replace ('/^#0\s+' . __FUNCTION__ . "[^\n]*\n/", '', $trace, 1); + + // Renumber backtrace items. + $trace = preg_replace ('/^#(\d+)/me', '\'#\' . ($1 - 1)', $trace); + + $trace = wordwrap($trace, 123, "' . HtmlFilters::nl2br($part) . '
'); + } + return implode("\n", $result); + } + + protected static function htmlAttribute($attrs = array(), $data = array()) { + $attrs = self::extract(array_merge(array('id', 'class', 'title', "style"), $attrs), $data); + + $result = array(); + foreach ($attrs as $name => $value) { + $result[] = "{$name}=\"{$value}\""; + } + return join(' ', $result); + } + + protected static function extract($attrs = array(), $data=array()) { + $result = array(); + if (empty($data)) return array(); + foreach($data as $k => $e) { + if (in_array($k, $attrs)) $result[$k] = $e; + } + return $result; + } +} + +class DatetimeFilters extends FilterCollection { + static function date($time, $format = 'jS F Y H:i') { + if ($time instanceof DateTime) + $time = (int) $time->format('U'); + if (!is_numeric($time)) + $time = strtotime($time); + + return date($format, $time); + } + + static function relative_time($timestamp, $format = 'g:iA') { + if ($timestamp instanceof DateTime) + $timestamp = (int) $timestamp->format('U'); + + $timestamp = is_numeric($timestamp) ? $timestamp: strtotime($timestamp); + + $time = mktime(0, 0, 0); + $delta = time() - $timestamp; + $string = ''; + + if ($timestamp < $time - 86400) { + return date("F j, Y, g:i a", $timestamp); + } + if ($delta > 86400 && $timestamp < $time) { + return "Yesterday at " . date("g:i a", $timestamp); + } + + if ($delta > 7200) + $string .= floor($delta / 3600) . " hours, "; + else if ($delta > 3660) + $string .= "1 hour, "; + else if ($delta >= 3600) + $string .= "1 hour "; + $delta %= 3600; + + if ($delta > 60) + $string .= floor($delta / 60) . " minutes "; + else + $string .= $delta . " seconds "; + return "$string ago"; + } + + static function relative_date($time) { + if ($time instanceof DateTime) + $time = (int) $time->format('U'); + + $time = is_numeric($time) ? $time: strtotime($time); + $today = strtotime(date('M j, Y')); + $reldays = ($time - $today)/86400; + + if ($reldays >= 0 && $reldays < 1) + return 'today'; + else if ($reldays >= 1 && $reldays < 2) + return 'tomorrow'; + else if ($reldays >= -1 && $reldays < 0) + return 'yesterday'; + + if (abs($reldays) < 7) { + if ($reldays > 0) { + $reldays = floor($reldays); + return 'in ' . $reldays . ' day' . ($reldays != 1 ? 's' : ''); + } else { + $reldays = abs(floor($reldays)); + return $reldays . ' day' . ($reldays != 1 ? 's' : '') . ' ago'; + } + } + if (abs($reldays) < 182) + return date('l, F j',$time ? $time : time()); + else + return date('l, F j, Y',$time ? $time : time()); + } + + static function relative_datetime($time) { + $date = self::relative_date($time); + + if ($date === 'today') + return self::relative_time($time); + + return $date; + } +} + +/* Ultizie php funciton as Filters */ +h2o::addFilter(array('md5', 'sha1', 'numberformat'=>'number_format', 'wordwrap', 'trim', 'upper' => 'strtoupper', 'lower' => 'strtolower')); + +/* Add filter collections */ +h2o::addFilter(array('CoreFilters', 'StringFilters', 'NumberFilters', 'DatetimeFilters', 'HtmlFilters')); + +/* Alias default to set_default */ +h2o::addFilter('default', array('CoreFilters', 'set_default')); + +?> diff --git a/system/vendor/h2o/loaders.php b/system/vendor/h2o/loaders.php new file mode 100644 index 0000000..1a5975a --- /dev/null +++ b/system/vendor/h2o/loaders.php @@ -0,0 +1,290 @@ +searchpath = (array) $searchpath; + $this->setOptions($options); + } + + function setOptions($options = array()) { + if (isset($options['cache']) && $options['cache']) { + $this->cache = h2o_cache($options); + } + } + + function read($filename) { + + if (!is_file($filename)) + $filename = $this->get_template_path($this->searchpath,$filename); + + if (is_file($filename)) { + $source = file_get_contents($filename); + return $this->runtime->parse($source); + } else { + throw new TemplateNotFound($filename); + } + } + + function get_template_path($search_path, $filename){ + + + for ($i=0 ; $i < count($search_path) ; $i++) + { + + if(file_exists($search_path[$i] . $filename)) { + $filename = $search_path[$i] . $filename; + return $filename; + break; + } else { + continue; + } + + } + + throw new Exception('TemplateNotFound - Looked for template: ' . $filename); + + + + } + + function read_cache($filename) { + if (!$this->cache){ + $filename = $this->get_template_path($this->searchpath,$filename); + return $this->read($filename); + } + + if (!is_file($filename)){ + $filename = $this->get_template_path($this->searchpath,$filename); + } + + $filename = realpath($filename); + + $cache = md5($filename); + $object = $this->cache->read($cache); + $this->cached = $object && !$this->expired($object); + + if (!$this->cached) { + $nodelist = $this->read($filename); + $object = (object) array( + 'filename' => $filename, + 'content' => serialize($nodelist), + 'created' => time(), + 'templates' => $nodelist->parser->storage['templates'], + 'included' => $nodelist->parser->storage['included'] + array_values(h2o::$extensions) + ); + $this->cache->write($cache, $object); + } else { + foreach($object->included as $ext => $file) { + include_once (h2o::$extensions[$ext] = $file); + } + } + return unserialize($object->content); + } + + function flush_cache() { + $this->cache->flush(); + } + + function expired($object) { + if (!$object) return false; + + $files = array_merge(array($object->filename), $object->templates); + foreach ($files as $file) { + if (!is_file($file)) + $file = $this->get_template_path($this->searchpath, $file); + + if ($object->created < filemtime($file)) + return true; + } + return false; + } +} + +function file_loader($file) { + return new H2o_File_Loader($file); +} + +class H2o_Hash_Loader { + + function __construct($scope, $options = array()) { + $this->scope = $scope; + } + + function setOptions() {} + + function read($file) { + if (!isset($this->scope[$file])) + throw new TemplateNotFound; + return $this->runtime->parse($this->scope[$file], $file); + } + + function read_cache($file) { + return $this->read($file); + } +} + +function hash_loader($hash = array()) { + return new H2o_Hash_Loader($hash); +} + +/** + * Cache subsystem + * + */ +function h2o_cache($options = array()) { + $type = $options['cache']; + $className = "H2o_".ucwords($type)."_Cache"; + + if (class_exists($className, false)) { + return new $className($options); + } + return false; +} + +class H2o_File_Cache { + var $ttl = 3600; + var $prefix = 'h2o_'; + + function __construct($options = array()) { + if (isset($options['cache_dir']) && is_writable($options['cache_dir'])) { + $path = $options['cache_dir']; + } else { + $path = dirname($tmp = tempnam(uniqid(rand(), true), '')); + + if (file_exists($tmp)) unlink($tmp); + } + if (isset($options['cache_ttl'])) { + $this->ttl = $options['cache_ttl']; + } + if(isset($options['cache_prefix'])) { + $this->prefix = $options['cache_prefix']; + } + + $this->path = realpath($path). DS; + } + + function read($filename) { + if (!file_exists($this->path . $this->prefix. $filename)) + return false; + + $content = file_get_contents($this->path . $this->prefix. $filename); + $expires = (int)substr($content, 0, 10); + + if (time() >= $expires) + return false; + return unserialize(trim(substr($content, 10))); + } + + function write($filename, &$object) { + $expires = time() + $this->ttl; + $content = $expires . serialize($object); + return file_put_contents($this->path . $this->prefix. $filename, $content); + } + + function flush() { + foreach (glob($this->path. $this->prefix. '*') as $file) { + @unlink($file); + } + } +} + +class H2o_Apc_Cache { + var $ttl = 3600; + var $prefix = 'h2o_'; + + function __construct($options = array()) { + if (!function_exists('apc_add')) + throw new Exception('APC extension needs to be loaded to use APC cache'); + + if (isset($options['cache_ttl'])) { + $this->ttl = $options['cache_ttl']; + } + if(isset($options['cache_prefix'])) { + $this->prefix = $options['cache_prefix']; + } + } + + function read($filename) { + return apc_fetch($this->prefix.$filename); + } + + function write($filename, $object) { + return apc_store($this->prefix.$filename, $object, $this->ttl); + } + + function flush() { + return apc_clear_cache('user'); + } +} + + +class H2o_Memcache_Cache { + var $ttl = 3600; + var $prefix = 'h2o_'; + /** + * @var host default is file socket + */ + var $host = 'unix:///tmp/memcached.sock'; + var $port = 0; + var $object; + function __construct( $scope, $options = array() ) { + if ( !function_exists( 'memcache_set' ) ) + throw new Exception( 'Memcache extension needs to be loaded to use memcache' ); + + if ( isset( $options['cache_ttl'] ) ) { + $this->ttl = $options['cache_ttl']; + } + if( isset( $options['cache_prefix'] ) ) { + $this->prefix = $options['cache_prefix']; + } + + if( isset( $options['host'] ) ) { + $this->host = $options['host']; + } + + if( isset( $options['port'] ) ) { + $this->port = $options['port']; + } + + $this->object = memcache_connect( $this->host, $this->port ); + } + + function read( $filename ){ + return memcache_get( $this->object, $this->prefix.$filename ); + } + + function write( $filename, $content ) { + return memcache_set( $this->object,$this->prefix.$filename,$content , MEMCACHE_COMPRESSED,$this->ttl ); + } + + function flush(){ + return memcache_flush( $this->object ); + } +} \ No newline at end of file diff --git a/system/vendor/h2o/nodes.php b/system/vendor/h2o/nodes.php new file mode 100644 index 0000000..9368ea6 --- /dev/null +++ b/system/vendor/h2o/nodes.php @@ -0,0 +1,84 @@ +parser = $parser; + if (is_null($initial)) + $initial = array(); + $this->list = $initial; + $this->position = $position; + } + + function render($context, $stream) { + foreach($this->list as $node) { + $node->render($context, $stream); + } + } + + function append($node) { + array_push($this->list, $node); + } + + function extend($nodes) { + array_merge($this->list, $nodes); + } + + function getLength() { + return count($this->list); + } + + function getIterator() { + return new ArrayIterator( $this->list ); + } +} + +class VariableNode extends H2o_Node { + private $filters = array(); + var $variable; + + function __construct($variable, $filters, $position = 0) { + if (!empty($filters)) + $this->filters = $filters; + $this->variable = $variable; + } + + function render($context, $stream) { + $value = $context->resolve($this->variable); + $value = $context->escape($value, $this->variable); + $stream->write($value); + } +} + +class CommentNode extends H2o_Node {} + +class TextNode extends H2o_Node { + var $content; + function __construct($content, $position = 0) { + $this->content = $content; + $this->position = $position; + } + + function render($context, $stream) { + $stream->write($this->content); + } + + function is_blank() { + return strlen(trim($this->content)); + } +} + + +?> \ No newline at end of file diff --git a/system/vendor/h2o/parser.php b/system/vendor/h2o/parser.php new file mode 100644 index 0000000..530d00f --- /dev/null +++ b/system/vendor/h2o/parser.php @@ -0,0 +1,290 @@ +options = $options; + + $trim = ''; + if ($this->options['TRIM_TAGS']) + $trim = '(?:\r?\n)?'; + + $this->pattern = ('/\G(.*?)(?:' . + preg_quote($this->options['BLOCK_START']). '(.*?)' .preg_quote($this->options['BLOCK_END']) . $trim . '|' . + preg_quote($this->options['VARIABLE_START']). '(.*?)' .preg_quote($this->options['VARIABLE_END']) . '|' . + preg_quote($this->options['COMMENT_START']). '(.*?)' .preg_quote($this->options['COMMENT_END']) . $trim . ')/sm' + ); + } + + function tokenize($source) { + $result = new TokenStream; + $pos = 0; + $matches = array(); + preg_match_all($this->pattern, $source, $matches, PREG_SET_ORDER); + + foreach ($matches as $match) { + if ($match[1]) + $result->feed('text', $match[1], $pos); + $tagpos = $pos + strlen($match[1]); + if ($match[2]) + $result->feed('block', trim($match[2]), $tagpos); + elseif ($match[3]) + $result->feed('variable', trim($match[3]), $tagpos); + elseif ($match[4]) + $result->feed('comment', trim($match[4]), $tagpos); + $pos += strlen($match[0]); + } + if ($pos < strlen($source)){ + $result->feed('text', substr($source, $pos), $pos); + } + $result->close(); + return $result; + } +} + +class H2o_Parser { + var $first; + var $storage = array(); + var $filename; + var $runtime; + + function __construct($source, $filename, $runtime, $options) { + $this->options = $options; + //$this->source = $source; + $this->runtime = $runtime; + $this->filename = $filename; + $this->first = true; + + $this->lexer = new H2o_Lexer($options); + $this->tokenstream = $this->lexer->tokenize($source); + $this->storage = array( + 'blocks' => array(), + 'templates' => array(), + 'included' => array() + ); + } + + function &parse() { + $until = func_get_args(); + $nodelist = new NodeList($this); + while($token = $this->tokenstream->next()) { + //$token = $this->tokenstream->current(); + switch($token->type) { + case 'text' : + $node = new TextNode($token->content, $token->position); + break; + case 'variable' : + $args = H2o_Parser::parseArguments($token->content, $token->position); + $variable = array_shift($args); + $filters = $args; + $node = new VariableNode($variable, $filters, $token->position); + break; + case 'comment' : + $node = new CommentNode($token->content); + break; + case 'block' : + if (in_array($token->content, $until)) { + $this->token = $token; + return $nodelist; + } + $temp = preg_split('/\s+/',$token->content, 2); + $name = $temp[0]; + $args = (count($temp) > 1 ? $temp[1] : null); + $node = H2o::createTag($name, $args, $this, $token->position); + $this->token = $token; + } + $this->searching = join(',',$until); + $this->first = false; + $nodelist->append($node); + } + + if ($until) { + throw new TemplateSyntaxError('Unclose tag, expecting '. $until[0]); + } + return $nodelist; + } + + function skipTo($until) { + $this->parse($until); + return null; + } + + # Parse arguments + static function parseArguments($source = null, $fpos = 0){ + $parser = new ArgumentLexer($source, $fpos); + $result = array(); + $current_buffer = &$result; + $filter_buffer = array(); + $tokens = $parser->parse(); + foreach ($tokens as $token) { + list($token, $data) = $token; + if ($token == 'filter_start') { + $filter_buffer = array(); + $current_buffer = &$filter_buffer; + } + elseif ($token == 'filter_end') { + if (count($filter_buffer)) { + + $i = count($result)-1; + if ( is_array($result[$i]) ) $result[$i]['filters'][] = $filter_buffer; + else $result[$i] = array(0 => $result[$i], 'filters' => array($filter_buffer)); + } + $current_buffer = &$result; + } + elseif ($token == 'boolean') { + $current_buffer[] = ($data === 'true'? true : false); + } + elseif ($token == 'name') { + $current_buffer[] = symbol($data); + } + elseif ($token == 'number' || $token == 'string') { + $current_buffer[] = $data; + } + elseif ($token == 'named_argument') { + $last = $current_buffer[count($current_buffer) - 1]; + if (!is_array($last)) + $current_buffer[] = array(); + + $namedArgs =& $current_buffer[count($current_buffer) - 1]; + list($name,$value) = array_map('trim', explode(':', $data, 2)); + + # if argument value is variable mark it + $value = self::parseArguments($value); + $namedArgs[$name] = $value[0]; + } + elseif( $token == 'operator') { + $current_buffer[] = array('operator'=>$data); + } + } + return $result; + } +} + +class H2O_RE { + static $whitespace, $seperator, $parentheses, $pipe, $filter_end, $operator, $boolean, $number, $string, $i18n_string, $name, $named_args; + + static function init() { + $r = 'strip_regex'; + + self::$whitespace = '/\s+/m'; + self::$parentheses = '/\(|\)/m'; + self::$filter_end = '/;/'; + self::$boolean = '/true|false/'; + self::$seperator = '/,/'; + self::$pipe = '/\|/'; + self::$operator = '/\s?(>|<|>=|<=|!=|==|!|and |not |or )\s?/i'; + self::$number = '/\d+(\.\d*)?/'; + self::$name = '/[a-zA-Z][a-zA-Z0-9-_]*(?:\.[a-zA-Z_0-9][a-zA-Z0-9_-]*)*/'; + + self::$string = '/(?: + "([^"\\\\]*(?:\\\\.[^"\\\\]*)*)" | # Double Quote string + \'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\' # Single Quote String + )/xsm'; + self::$i18n_string = "/_\({$r(self::$string)}\) | {$r(self::$string)}/xsm"; + + self::$named_args = "{ + ({$r(self::$name)})(?:{$r(self::$whitespace)})? + : + (?:{$r(self::$whitespace)})?({$r(self::$i18n_string)}|{$r(self::$number)}|{$r(self::$name)}) + }x"; + } +} +H2O_RE::init(); + +class ArgumentLexer { + private $source; + private $match; + private $pos = 0, $fpos, $eos; + private $operator_map = array( + '!' => 'not', '!='=> 'ne', '==' => 'eq', '>' => 'gt', '<' => 'lt', '<=' => 'le', '>=' => 'ge' + ); + + function __construct($source, $fpos = 0){ + if (!is_null($source)) + $this->source = $source; + $this->fpos=$fpos; + } + + function parse(){ + $result = array(); + $filtering = false; + while (!$this->eos()) { + $this->scan(H2O_RE::$whitespace); + if (!$filtering) { + if ($this->scan(H2O_RE::$operator)){ + $operator = trim($this->match); + if(isset($this->operator_map[$operator])) + $operator = $this->operator_map[$operator]; + $result[] = array('operator', $operator); + } + elseif ($this->scan(H2O_RE::$boolean)) + $result[] = array('boolean', $this->match); + elseif ($this->scan(H2O_RE::$named_args)) + $result[] = array('named_argument', $this->match); + elseif ($this->scan(H2O_RE::$name)) + $result[] = array('name', $this->match); + elseif ($this->scan(H2O_RE::$pipe)) { + $filtering = true; + $result[] = array('filter_start', $this->match); + } + elseif ($this->scan(H2O_RE::$seperator)) + $result[] = array('separator', null); + elseif ($this->scan(H2O_RE::$i18n_string)) + $result[] = array('string', $this->match); + elseif ($this->scan(H2O_RE::$number)) + $result[] = array('number', $this->match); + else + throw new TemplateSyntaxError('unexpected character in filters : "'. $this->source[$this->pos]. '" at '.$this->getPosition()); + } + else { + // parse filters, with chaining and ";" as filter end character + if ($this->scan(H2O_RE::$pipe)) { + $result[] = array('filter_end', null); + $result[] = array('filter_start', null); + } + elseif ($this->scan(H2O_RE::$seperator)) + $result[] = array('separator', null); + elseif ($this->scan(H2O_RE::$filter_end)) { + $result[] = array('filter_end', null); + $filtering = false; + } + elseif ($this->scan(H2O_RE::$boolean)) + $result[] = array('boolean', $this->match); + elseif ($this->scan(H2O_RE::$named_args)) + $result[] = array('named_argument', $this->match); + elseif ($this->scan(H2O_RE::$name)) + $result[] = array('name', $this->match); + elseif ($this->scan(H2O_RE::$i18n_string)) + $result[] = array('string', $this->match); + elseif ($this->scan(H2O_RE::$number)) + $result[] = array('number', $this->match); + else + throw new TemplateSyntaxError('unexpected character in filters : "'. $this->source[$this->pos]. '" at '.$this->getPosition()); + } + } + // if we are still in the filter state, we add a filter_end token. + if ($filtering) + $result[] = array('filter_end', null); + return $result; + } + + # String scanner + function scan($regexp) { + if (preg_match($regexp . 'A', $this->source, $match, null, $this->pos)) { + $this->match = $match[0]; + $this->pos += strlen($this->match); + return true; + } + return false; + } + + function eos() { + return $this->pos >= strlen($this->source); + } + + /** + * return the position in the template + */ + function getPosition() { + return $this->fpos + $this->pos; + } +} +?> diff --git a/system/vendor/h2o/tags.php b/system/vendor/h2o/tags.php new file mode 100644 index 0000000..ea2fdbf --- /dev/null +++ b/system/vendor/h2o/tags.php @@ -0,0 +1,483 @@ +nodelist_true = $parser->parse('endifchanged', 'else'); + + if ($parser->token->content === 'else') + $this->nodelist_false = $parser->parse('endifchanged'); + + $this->_varlist = current(H2o_Parser::parseArguments($argstring)); + + if (!$this->_varlist) + throw new TemplateSyntaxError('H2o doesn\'t support lazy ifchanged yet. Please, supply a variable.'); + + } + + function render($context, $stream) { + + if ($this->_varlist) { + $compare_to = $context->resolve($this->_varlist); + } else { + /** + * @todo Rendering method $this->nodelist_true->render() should return a result. + * Further more $compare_to variable should be set to this result. + */ + $compare_to = ''; + } + + if ($compare_to != $this->_last_seen) { + $this->_last_seen = $compare_to; + $this->nodelist_true->render($context, $stream); + } elseif ($this->nodelist_false) { + $this->nodelist_false->render($context, $stream); + } + + } +} + +class If_Tag extends H2o_Node { + private $body; + private $else; + private $negate; + + function __construct($argstring, $parser, $position = 0) { + if (preg_match('/\s(and|or)\s/', $argstring)) + throw new TemplateSyntaxError('H2o doesn\'t support multiple expressions'); + + $this->body = $parser->parse('endif', 'else'); + + if ($parser->token->content === 'else') + $this->else = $parser->parse('endif'); + + $this->args = H2o_Parser::parseArguments($argstring); + + $first = current($this->args); + if (isset($first['operator']) && $first['operator'] === 'not') { + array_shift($this->args); + $this->negate = true; + } + } + + function render($context, $stream) { + if ($this->test($context)) + $this->body->render($context, $stream); + elseif ($this->else) + $this->else->render($context, $stream); + } + + function test($context) { + $test = Evaluator::exec($this->args, $context); + return $this->negate? !$test : $test; + } +} + +class For_Tag extends H2o_Node { + public $position; + private $iteratable, $key, $item, $body, $else, $limit, $reversed; + private $syntax = '{ + ([a-zA-Z][a-zA-Z0-9-_]*)(?:,\s?([a-zA-Z][a-zA-Z0-9-_]*))? + \s+in\s+ + ([a-zA-Z][a-zA-Z0-9-_]*(?:\.[a-zA-Z_0-9][a-zA-Z0-9_-]*)*)\s* # Iteratable name + (?:limit\s*:\s*(\d+))?\s* + (reversed)? # Reverse keyword + }x'; + + function __construct($argstring, $parser, $position) { + if (!preg_match($this->syntax, $argstring, $match)) + throw new TemplateSyntaxError("Invalid for loop syntax "); + + $this->body = $parser->parse('endfor', 'else'); + + if ($parser->token->content === 'else') + $this->else = $parser->parse('endfor'); + + $match = array_pad($match, 6, ''); + list(,$this->key, $this->item, $this->iteratable, $this->limit, $this->reversed) = $match; + + if ($this->limit) + $this->limit = (int) $this->limit; + + # Swap value if no key found + if (!$this->item) { + list($this->key, $this->item) = array($this->item, $this->key); + } + $this->iteratable = symbol($this->iteratable); + $this->reversed = (bool) $this->reversed; + } + + function render($context, $stream) { + $iteratable = $context->resolve($this->iteratable); + + if ($this->reversed) + $iteratable = array_reverse($iteratable); + + if ($this->limit) + $iteratable = array_slice($iteratable, 0, $this->limit); + + $length = count($iteratable); + + if ($length) { + $parent = $context['loop']; + $context->push(); + $rev_count = $is_even = $idx = 0; + foreach($iteratable as $key => $value) { + $is_even = $idx % 2; + $rev_count = $length - $idx; + + if ($this->key) { + $context[$this->key] = $key; + } + $context[$this->item] = $value; + $context['loop'] = array( + 'parent' => $parent, + 'first' => $idx === 0, + 'last' => $rev_count === 1, + 'odd' => !$is_even, + 'even' => $is_even, + 'length' => $length, + 'counter' => $idx + 1, + 'counter0' => $idx, + 'revcounter' => $rev_count, + 'revcounter0' => $rev_count - 1 + ); + $this->body->render($context, $stream); + ++$idx; + } + $context->pop(); + } elseif ($this->else) + $this->else->render($context, $stream); + } +} + +class Block_Tag extends H2o_Node { + public $name; + public $position; + public $stack; + private $syntax = '/^[a-zA-Z_][a-zA-Z0-9_-]*$/'; + + function __construct($argstring, $parser, $position) { + if (!preg_match($this->syntax, $argstring)) + throw new TemplateSyntaxError('Block tag expects a name, example: block [content]'); + + $this->name = $argstring; + + if (isset($parser->storage['blocks'][$this->name])) + throw new TemplateSyntaxError('Block name exists, Please select a different block name'); + + $this->filename = $parser->filename; + $this->stack = array($parser->parse('endblock', "endblock {$this->name}")); + + $parser->storage['blocks'][$this->name] = $this; + $this->position = $position; + } + + function addLayer(&$nodelist) { + $nodelist->parent = $this; + array_push($this->stack, $nodelist); + } + + function render($context, $stream, $index = 1) { + $key = count($this->stack) - $index; + + if (isset($this->stack[$key])) { + $context->push(); + $context['block'] = new BlockContext($this, $context, $index); + $this->stack[$key]->render($context, $stream); + $context->pop(); + } + } +} + +class Extends_Tag extends H2o_Node { + public $filename; + public $position; + public $nodelist; + private $syntax = '/^["\'](.*?)["\']$/'; + + function __construct($argstring, $parser, $position = 0) { + if (!$parser->first) + throw new TemplateSyntaxError('extends must be first in file'); + + if (!preg_match($this->syntax, $argstring)) + throw new TemplatesyntaxError('filename must be quoted'); + + $this->filename = stripcslashes(substr($argstring, 1, -1)); + + # Parse the current template + $parser->parse(); + + # Parse parent template + $this->nodelist = $parser->runtime->loadSubTemplate($this->filename, $parser->options); + $parser->storage['templates'] = array_merge( + $parser->storage['templates'], $this->nodelist->parser->storage['templates'] + ); + $parser->storage['templates'][] = $this->filename; + + if (!isset($this->nodelist->parser->storage['blocks']) || !isset($parser->storage['blocks'])) + return ; + + # Blocks of parent template + $blocks =& $this->nodelist->parser->storage['blocks']; + + # Push child blocks on top of parent blocks + foreach($parser->storage['blocks'] as $name => &$block) { + if (isset($blocks[$name])) { + $blocks[$name]->addLayer($block); + } + } + } + + function render($context, $stream) { + $this->nodelist->render($context, $stream); + } +} + +/** + * include tag + * + * Usage: + * + * Simple inclusion + * {% include "./subtemplate.html" %} + * + * + * Inclusion with additional context variables passing: + * {% include "./subtemplate.html" with foo=bar spam="eggs" %} + * + * Note: Double quotes matter. In this example 'foo' template variable of subtemplate.html + * would be initialized with 'bar' variable contents (from main template context), + * while 'spam' template variable of subtemplate.html would be set to simple string ('eggs'). + * + */ +class Include_Tag extends H2o_Node { + private $nodelist; + private $syntax = '/^["\'](.*?)["\'](\s+with\s+(.+))?$/'; + private $_additional_context = array(); + + function __construct($argstring, $parser, $position = 0) { + if (!preg_match($this->syntax, $argstring, $matches)) { + throw new TemplateSyntaxError(); + } + + $matches_count = count($matches); + + if ($matches_count > 2) { + // "with" statement supplied. + $with_vars = explode(' ', $matches[3]); + foreach ($with_vars as $var_str) { + $eq_pos = strpos($var_str, '='); + $this->_additional_context[substr($var_str, 0, $eq_pos)] = substr($var_str, $eq_pos+1); + } + } + + $this->filename = stripcslashes($matches[1]); + $this->nodelist = $parser->runtime->loadSubTemplate($this->filename, $parser->options); + $parser->storage['templates'] = array_merge( + $this->nodelist->parser->storage['templates'], $parser->storage['templates'] + ); + $parser->storage['templates'][] = $this->filename; + } + + function render($context, $stream) { + foreach ($this->_additional_context as $key => $value) { + if (strpos($value, '"') === false) { + // Context variable supplied as value. Needs to be resolved. + $value = $context->getVariable($value); + } else { + $value = trim($value, '"'); + } + $context[$key] = $value; + } + + $this->nodelist->render($context, $stream); + } +} + +class With_Tag extends H2o_Node { + public $position; + private $variable, $shortcut; + private $nodelist; + private $syntax = '/^([\w]+(:?\.[\w\d]+)*)\s+as\s+([\w]+(:?\.[\w\d]+)?)$/'; + + function __construct($argstring, $parser, $position = 0) { + if (!preg_match($this->syntax, $argstring, $matches)) + throw new TemplateSyntaxError('Invalid with tag syntax'); + + # extract the long name and shortcut + list(,$this->variable, ,$this->shortcut) = $matches; + $this->nodelist = $parser->parse('endwith'); + } + + function render($context, $stream) { + $variable = $context->getVariable($this->variable); + + $context->push(array($this->shortcut => $variable)); + $this->nodelist->render($context, $stream); + $context->pop(); + } +} + +class Cycle_Tag extends H2o_Node { + private $uid; + private $sequence; + + function __construct($argstring, $parser, $pos) { + $args = h2o_parser::parseArguments($argstring); + + if (count($args) < 2) { + throw new Exception('Cycle tag require more than two items'); + } + $this->sequence = $args; + $this->uid = '__cycle__'.$pos; + } + + function render($context, $stream) { + if (!is_null($item = $context->getVariable($this->uid))) { + $item = ($item + 1) % count($this->sequence); + } else { + $item = 0; + } + $stream->write($context->resolve($this->sequence[$item])); + $context->set($this->uid, $item); + } +} + +class Load_Tag extends H2o_Node { + public $position; + private $searchpath = array(H2O_ROOT); + private $extension; + + function __construct($argstring, $parser, $pos = 0) { + $this->extension = stripcslashes(preg_replace("/^[\"'](.*)[\"']$/", "$1", $argstring)); + + if ($parser->runtime->searchpath) + $this->appendPath($parser->runtime->searchpath); + + $parser->storage['included'][$this->extension] = $file = $this->load(); + $this->position = $pos; + } + + function render($context, $stream) { + $this->load(); + } + + function appendPath($path) { + $this->searchpath[] = $path; + } + + private function load() { + if (isset(h2o::$extensions[$this->extension])) { + return true; + } + foreach($this->searchpath as $path) { + $file = $path.'ext'.DS.$this->extension.'.php'; + if (is_file($file)) { + h2o::load($this->extension, $file); + return $file; + } + } + throw new H2o_Error( + "Extension: {$this->extension} cannot be loaded, please confirm it exist in extension path" + ); + } +} + +class Debug_Tag extends H2o_Node { + private $argument; + function __construct($argstring, $parser, $pos = 0) { + $this->argument = $argstring; + } + + function render($context, $stream) { + if ($this->argument) { + $object = $context->resolve(symbol($this->argument)); + } else { + $object = $context->scopes[0]; + } + $output = "" . htmlspecialchars( print_r($object, true) ) . ""; + $stream->write($output); + } +} + +class Comment_Tag extends H2o_Node { + function __construct($argstring, $parser, $position) { + $parser->parse('endcomment'); + } + + function render($context, $stream, $index = 1) { + } +} + +class Now_Tag extends H2o_Node { + function __construct($argstring, $parser, $pos=0) { + $this->format = $argstring; + if (!$this->format) { + $this->format = "D M j G:i:s T Y"; + } + } + + function render($contxt, $stream) { + $time = date($this->format); + $stream->write($time); + } +} + +class Autoescape_Tag extends H2o_Node { + protected $enable; + + function __construct($argstring, $parser, $pos = 0) { + if ($argstring === 'on') + $this->enable = true; + elseif ($argstring === 'off') + $this->enable = false; + else throw new H2o_Error( + "Invalid syntax : autoescape on|off " + ); + } + + function render($context, $stream) { + $context->autoescape = $this->enable; + } +} + +class Csrf_token_Tag extends H2o_Node { + function render($context, $stream) { + $token = ""; + if (isset($_COOKIE["csrftoken"])) + $token = $_COOKIE["csrftoken"]; + else { + global $SECRET_KEY; + if (defined('SECRET_KEY')) + $token = md5(mt_rand() . SECRET_KEY); + else + $token = md5(mt_rand()); + } + setcookie("csrftoken", $token, time()+60*60*24*365, "/"); + $stream->write(""); + } +} + +H2o::addTag(array('block', 'extends', 'include', 'if', 'ifchanged', 'for', 'with', 'cycle', 'load', 'debug', 'comment', 'now', 'autoescape', 'csrf_token'));