Adding template engine

Adding error handling
Adding basic SMTP class
core routing is working
master
Nathan Adams 2014-04-19 15:11:53 -05:00
parent 703a7d5bbd
commit a7c962462d
28 changed files with 3126 additions and 45 deletions

View File

@ -1,2 +1,5 @@
RewriteEngine on
RewriteRule ^(.*)$ /index.php/$1 [L]
RewriteBase /test
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]

View File

@ -0,0 +1 @@
Deny from all

View File

@ -0,0 +1,5 @@
<?php
$config["ADMINS"] = array();
return $config;

View File

@ -0,0 +1 @@
Deny from all

View File

@ -0,0 +1 @@
Deny from all

View File

@ -0,0 +1 @@
Deny from all

View File

@ -1,10 +1,11 @@
<?php
require('system/engine/core.php');
foreach (glob("system/vendor/*.php") as $filename)
{
include $filename;
}
require('system/engine/core.php');
$core = new HF_Core();
$core->run();

1
system/.htaccess 100644
View File

@ -0,0 +1 @@
Deny from all

View File

@ -0,0 +1 @@
Deny from all

View File

@ -0,0 +1,80 @@
<?php
// Based on tutorial from here: https://portal.cyberhostpro.com/knowledgebase/170/PHP-Mail-Script-with-SMTP-Authentication.html
class HF_SMTP
{
private $from = "";
private $to = "";
private $subject = "";
private $msg = "";
private $user = null;
private $password = null;
private $port = 25;
private $server = "localhost";
private $smtpserverconn = null;
public function __construct($from, $to, $subject, $msg, $server = "localhost", $user = null, $password = null, $port = 25)
{
$this->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;
}
}

View File

@ -0,0 +1,14 @@
<?php
$config["DEFAULT_ROUTE"] = "main";
$config["USE_H20_TPL"] = true;
$config["SMTP_SERVER"] = "localhost";
$config["SMTP_USER"] = "";
$config["SMTP_PASS"] = "";
$config["SMTP_PORT"] = 25;
$config["SMTP_FROM"] = "HF-noreply@yoursite.com";
$config["DEBUG"] = true;
$config["USE_HF_SMTP"] = true;
$config["SITE_NAME"] = "default";
return $config;

View File

@ -0,0 +1,15 @@
<?php
class HF_Controller
{
protected $config;
protected $tpl;
protected $core;
public function __construct($config, $core, $tpl = null)
{
$this->config = $config;
$this->tpl = $tpl;
$this->core = $core;
}
}

View File

@ -1,63 +1,235 @@
<?php
include "system/engine/controller.php";
include "system/engine/SMTP.php";
include "system/engine/exceptions.php";
class HF_Core
{
private $class;
private $method;
private $classname;
private $args = array();
private $config = array();
private $tpl;
public function __construct()
{
$this->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);
}
}
}

View File

@ -0,0 +1,18 @@
<?php
//Original idea by http://www.php.net/manual/en/function.set-error-handler.php#112881
class WarningException extends ErrorException {}
class ParseException extends ErrorException {}
class NoticeException extends ErrorException {}
class CoreErrorException extends ErrorException {}
class CoreWarningException extends ErrorException {}
class CompileErrorException extends ErrorException {}
class CompileWarningException extends ErrorException {}
class UserErrorException extends ErrorException {}
class UserWarningException extends ErrorException {}
class UserNoticeException extends ErrorException {}
class StrictException extends ErrorException {}
class RecoverableErrorException extends ErrorException {}
class DeprecatedException extends ErrorException {}
class UserDeprecatedException extends ErrorException {}

View File

@ -0,0 +1,15 @@
<?php
class HF_Status extends HF_Controller
{
public function Status404()
{
echo "Page not found!";
}
public function Status500()
{
echo "System error";
}
}

1
system/vendor/.htaccess vendored 100644
View File

@ -0,0 +1 @@
Deny from all

453
system/vendor/README.md vendored 100644
View File

@ -0,0 +1,453 @@
H2O template markup
========================
Being a martial arts fan, I borrow a quote.
H2O template
------------------------
H2O is markup language for PHP that has taken a lot of inspiration from Django.
__Features__
* Readable and human-friendly syntax.
* Easy to use and maintain
* Encourages reuse in templates by allowing template inclusion and inheritance.
* Highly extensible through filters, tags, and template extensions.
* Includes a rich set of filters and tags for string formatting, HTML helpers and
internationalization support.
Requirement
------------------------
- PHP 5.1 +
News
------------------------
- version 0.4
1. **Breaking changes** autoescape is now turned on by default
2. Improved searchpath and file loading handling
3. Improved handling on PHP overloaded objects
4. Plenty of bug fixes
- version 0.3
1. Support internationalized templates and translation parsing toolkit
2. Performance optimization on context lookup
3. Fixed operator parsing
Getting started
------------------------
### Getting h2o
Download
[<img src="http://github.com/images/modules/download/zip.png">](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*
<body>
<head><title>Hello world</title></head>
<body>
Greetings {{ name }}
</body>
</body>
*index.php*
<?php
require 'h2o/h2o.php';
$h2o = new h2o('templates/index.html');
echo $h2o->render(array('name'=>'Peter Jackson'));
?>
Useful links
------------------------
* Please submit patches or bug reports to our [lighthouse bug tracker][issue].
* Check out our [Google group][group] for h2o-related discussion.
[issue]:http://idealian.lighthouseapp.com/projects/11041-h2o-template-language
[group]:http://groups.google.com/group/h2o-template-php
Syntax explanation
------------------------
## variable
`{{ variable }}`
Use dot (.) to access attributes of a variable
#### variable lookup order
1. key of an associative array
2. array-index
3. object attribute
4. object method call
**Example**
*in your template*
{{ person.name }}
*in php*
<?php
$h2o = new H2o('template.tpl');
$person =array(
'name' => 'Peter Jackson', 'age' => 25
);
$h2o->render(compact('person'));
?>
Let's say that you have assigned the value `Peter Jackson` to a 'person' variable in your php script. The following
variable tag will print out `Peter Jackson`.
## Filters
Filters are variable modifiers that manipulate or format the value of a variable.
A filter usually looks like this `{{ person.name|capitalize }}`, a pipe ( | )
character after a variable, plus a filter name, will cause H2O to apply the filter.
__Filter chaining__
![filter chaining](http://wiki.shopify.com/upload/8/8c/Filterchain.jpg)
Let me borrow the image from liquid template
You can chain multiple filters together and use a pipe ( | ) character to separate
them. `{{ document.body|escape|nl2br }}`
__Filter arguments__
Filters can accept arguments. For example:
`{{ document.description|truncate 20 }}`
will display the first 20 characters of the document's description.
Moreover, there are cases where you might want to pass in multiple arguments.
Use commas ( , ) to separate them:
`{{ person.bio|truncate 20, "..." }}`
__Filter named arguments__
h2o uses colons ( : ) for named arguments. These allow you to build 'optional argument' arrays.
`{{ '/images/logo.png' | image_tag width:450, height:250, alt:"company logo" }}`
The above code translated to php will be like the below snippet, which resembles what happens internally:
<?php
echo image_tag("/image/logo.png", array(
'width' => 450,
'height' => 250,
'alt'=>'company logo'
));
?>
Note: Difference with Django, Smarty
H2o does not use the colon ( : ) character to separate arguments for readability reasons,
H2o uses the comma ( , ) which is more logical.
## Tag
`{% tag %}`
Tags are very powerful, as they control the logical flow and structure of a template,
There are inline tags `{% inline_tag %}` or tags that requires a
close tag. For example: `{% if condition %} ... {% endif %}`
### The "if" tag
`if` tags evaluate either a variable or a simple expression. If the result of the `if`
expression is *true*, then the contents of the `if` block will be allowed to render.
{% if person.is_adult %}
You are old enough.
{% else %}
sorry, you are too young for that.
{% endif %}
### The "for" tag
`for` tags allow iteratation over an array of items.
{% for task in tasks %}
{{ task }}
{% endfor %}
The above snippet will print out each "task" in the "tasks" array.
Template inheritance
------------------------
H2o supports template inheritance. Inheritance allows you to factor out a lot
of common code that would otherwise be duplicated across most of your templates.
Template inheritance is implemented using the `block` and `extends` tags, with child templates
*extending* their parent templates.
**Word of Caution:**
* H2o templates only support single inheritance (just like PHP!), and currently do not support deep inheritance chains.
Quote from the Django docs:
> ... a base skeleton template that contains all the common elements of your
> site and defines blocks that child templates can override.
*Example:*
_base.html_ - defines the base structure of our pages.
<html>
<head><title>{%block title %}This is a page title {% endblock %}</title></head>
<body>
<div id="content">
{% block content%}
<h1> Page title </h1>
<p> H2O template inheritance is a powerful tool </p>
{% endblock %}
</div>
<div id="sidebar">
{% block sidebar %}{% endblock %}
</div>
</body>
</html>
As you can see, the base template is a typical web page using a two column layout.
We defined two blocks (`content` and `sidebar`) and HTML code common across all of our pages.
_page.html_ - defines a page-specific template.
{% extends 'base.html' %}
{% block content %}
<h1> extended page </h1>
<p> Body of extended page </p>
{% 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.
<?php
$h2o = new H2o('template.tpl', array(
[option_name] => [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__
<?php
$loader = new H2o_File_Loader($custom_searchpath);
$template = new H2o('index.html', array('loader'=> $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.
<?php
$loader = dict_loader(array(
"index.html" => '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).
<?php
$template = new H2o('homepage.tpl', array(
'cache' => '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.
<?php
$template = new h2o('homepage.tpl', array('cache' => '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.

View File

@ -0,0 +1,66 @@
<?php
// Original http://stackoverflow.com/a/4282133/195722
function vdump() {
$args = func_get_args();
$backtrace = debug_backtrace();
$code = file($backtrace[0]['file']);
$ret = "<pre style='background: #eee; border: 1px solid #aaa; clear: both; overflow: auto; padding: 10px; text-align: left; margin-bottom: 5px'>";
$ret .- "<b>".htmlspecialchars(trim($code[$backtrace[0]['line']-1]))."</b>\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/', ' &rarr; <b style="color: #000">NULL</b>', $str);
$str = preg_replace('/}\n(\s+)\[/', "}\n\n".'$1[', $str);
$str = preg_replace('/ (float|int)\((\-?[\d\.]+)\)/', " <span style='color: #888'>$1</span> <b style='color: brown'>$2</b>", $str);
$str = preg_replace('/array\((\d+)\) {\s+}\n/', "<span style='color: #888'>array&bull;$1</span> <b style='color: brown'>[]</b>", $str);
$str = preg_replace('/ string\((\d+)\) \"(.*)\"/', " <span style='color: #888'>str&bull;$1</span> <b style='color: brown'>'$2'</b>", $str);
$str = preg_replace('/\[\"(.+)\"\] => /', "<span style='color: purple'>'$1'</span> &rarr; ", $str);
$str = preg_replace('/object\((\S+)\)#(\d+) \((\d+)\) {/', "<span style='color: #888'>obj&bull;$2</span> <b style='color: #0C9136'>$1[$3]</b> {", $str);
$str = str_replace("bool(false)", "<span style='color:#888'>bool&bull;</span><span style='color: red'>false</span>", $str);
$str = str_replace("bool(true)", "<span style='color:#888'>bool&bull;</span><span style='color: green'>true</span>", $str);
$ret .= $str;
$ret .= "</pre>";
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, "<br>");
return $trace;
}

266
system/vendor/h2o.php vendored 100644
View File

@ -0,0 +1,266 @@
<?php
define('H2O_VERSION', '0.3');
defined('DS') or define('DS', DIRECTORY_SEPARATOR);
defined('H2O_ROOT') or define('H2O_ROOT', dirname(__FILE__) . DS);
require H2O_ROOT.'h2o/datatype.php';
require H2O_ROOT.'h2o/loaders.php';
require H2O_ROOT.'h2o/nodes.php';
require H2O_ROOT.'h2o/tags.php';
require H2O_ROOT.'h2o/errors.php';
require H2O_ROOT.'h2o/filters.php';
require H2O_ROOT.'h2o/context.php';
/**
* Example:
* $h2o = new H2O('./template.html', array("loader"=>'file'));
*
*
* $h2o = new H2O('template.html', array("loader"=>'hash'));
*/
class H2o {
var $searchpath;
var $context;
var $loader = false;
static $tags = array();
static $filters = array();
static $extensions = array();
static function getOptions($options = array()) {
return array_merge(array(
'loader' => 'file',
'cache' => 'file', // file | apc | memcache
'cache_prefix' => 'h2o_',
'cache_ttl' => 3600, // file | apc | memcache
'searchpath' => false,
'autoescape' => true,
// Enviroment setting
'BLOCK_START' => '{%',
'BLOCK_END' => '%}',
'VARIABLE_START' => '{{',
'VARIABLE_END' => '}}',
'COMMENT_START' => '{*',
'COMMENT_END' => '*}',
'TRIM_TAGS' => true
), $options);
}
function __construct($file = null, $options = array()) {
# Init a environment
$this->options = $this->getOptions($options);
$loader = $this->options['loader'];
if (!$loader)
return true;
if (is_object($loader)) {
$this->loader = $loader;
$this->loader->setOptions($this->options);
} else {
$loader = "H2o_{$loader}_Loader";
if (!class_exists($loader, false))
throw new Exception('Invalid template loader');
if (isset($options['searchpath'])){
$this->searchpath = $options['searchpath'];
} else if ($file) {
$this->searchpath = dirname(realpath($file)).DS;
} else {
$this->searchpath = getcwd().DS;
}
$loader_searchpath = is_array($this->searchpath) ? $this->searchpath : array($this->searchpath);
$this->loader = new $loader($loader_searchpath, $this->options);
}
$this->loader->runtime = $this;
if (isset($options['i18n'])) {
h2o::load('i18n');
$this->i18n = new H2o_I18n($this->searchpath, $options['i18n']);
}
if ($file) {
$this->nodelist = $this->loadTemplate($file);
}
}
function loadTemplate($file) {
return $this->nodelist = $this->loader->read_cache($file);
}
function loadSubTemplate($file) {
return $this->loader->read($file);
}
# Build a finalized nodelist from template ready to be cached
function parse($source, $filename = '', $env = null) {
if (!$env)
$env = $this->options;
if (!class_exists('H2o_Parser', false))
require H2O_ROOT.'h2o/parser.php';
$parser = new H2o_Parser($source, $filename, $this, $env);
$nodelist = $parser->parse();
return $nodelist;
}
function set($context, $value = null) {
# replace with new context object
if (is_object($context) && $context instanceof H2o_Context) {
return $this->context = $context;
}
# Init context
if (!$this->context) {
$this->context = new H2o_Context($this->defaultContext(), $this->options);
}
# Extend or set value
if (is_array($context)) {
return $this->context->extend($context);
}
elseif (is_string($context)) {
return $this->context[$context] = $value;
}
return false;
}
# Render the nodelist
function render($context = array()) {
$this->set($context);
$this->stream = new StreamWriter;
$this->nodelist->render($this->context, $this->stream);
return $this->stream->close();
}
static function parseString($source, $options = array()) {
$instance = new H2o(null, array_merge($options, array('loader' => false)));
$instance->nodelist = $instance->parse($source);
return $instance;
}
static function &createTag($tag, $args = null, $parser, $position = 0) {
if (!isset(self::$tags[$tag])) {
throw new H2o_Error($tag . " tag doesn't exist");
}
$tagClass = self::$tags[$tag];
$tag = new $tagClass($args, $parser, $position);
return $tag;
}
/**
* Register a new tag
*
*
* h2o::addTag('tag_name', 'ClassName');
*
* h2o::addTag(array(
* 'tag_name' => 'MagClass',
* 'tag_name2' => 'TagClass2'
* ));
*
* h2o::addTag('tag_name'); // Tag_name_Tag
*
* h2o::addTag(array('tag_name',
* @param unknown_type $tag
* @param unknown_type $class
*/
static function addTag($tag, $class = null) {
$tags = array();
if (is_string($tag)) {
if (is_null($class))
$class = ucwords("{$tag}_Tag");
$tags[$tag] = $class;
} elseif (is_array($tag)) {
$tags = $tag;
}
foreach ($tags as $tag => $tagClass) {
if (is_integer($tag)) {
unset($tags[$tag]);
$tag = $tagClass;
$tagClass = ucwords("{$tagClass}_Tag");
}
if (!class_exists($tagClass, false)) {
throw new H2o_Error("{$tagClass} tag is not found");
}
$tags[$tag] = $tagClass;
}
self::$tags = array_merge(self::$tags, $tags);
}
/**
* Register a new filter to h2o runtime
*
* @param unknown_type $filter
* @param unknown_type $callback
* @return unknown
*/
static function addFilter($filter, $callback = null) {
if (is_array($filter)) {
$filters = $filter;
foreach($filters as $key => $filter) {
if (is_numeric($key))
$key = $filter;
self::addFilter($key, $filter);
}
return true;
} elseif (is_string($filter) && class_exists($filter, false) && is_subclass_of($filter, 'FilterCollection')) {
foreach (get_class_methods($filter) as $f) {
if (is_callable(array($filter, $f)))
self::$filters[$f] = array($filter, $f);
}
return true;
}
if (is_null($callback))
$callback = $filter;
if (!is_callable($callback)) {
return false;
}
self::$filters[$filter] = $callback;
}
static function addLookup($callback) {
if (is_callable($callback)) {
H2o_Context::$lookupTable[] = $callback;
} else die('damm it');
}
static function load($extension, $file = null) {
if (!$file) {
$file = H2O_ROOT.'ext'.DS.$extension.'.php';
}
if (is_file($file)) {
include_once ($file);
self::$extensions[$extension] = $file;
}
}
function defaultContext() {
return array('h2o' => new H2o_Info);
}
}
/**
* Convenient wrapper for loading template file or string
* @param $name
* @param $options - H2o options
* @return Instance of H2o Template
*/
function h2o($name, $options = array()) {
$is_file = '/([^\s]*?)(\.[^.\s]*$)/';
if (!preg_match($is_file, $name)) {
return H2o::parseString($name, $options);
}
$instance = new H2o($name, $options);
return $instance;
}
?>

1
system/vendor/h2o/.htaccess vendored 100644
View File

@ -0,0 +1 @@
Deny from all

266
system/vendor/h2o/context.php vendored 100644
View File

@ -0,0 +1,266 @@
<?php
/**
* Context object
* encapsulate context, resolve name
*/
class H2o_Context implements ArrayAccess {
public $safeClass = array('stdClass', 'BlockContext');
public $scopes;
public $options;
public $autoescape = true;
private $arrayMethods = array('first'=> 0, 'last'=> 1, 'length'=> 2, 'size'=> 3);
static $lookupTable = array();
function __construct($context = array(), $options = array()){
if (is_object($context))
$context = get_object_vars($context);
$this->scopes = array($context);
if (isset($options['safeClass']))
$this->safeClass = array_merge($this->safeClass, $options['safeClass']);
if (isset($options['autoescape']))
$this->autoescape = $options['autoescape'];
$this->options = $options;
}
function push($layer = array()){
return array_unshift($this->scopes, $layer);
}
/**
* pop the most recent layer
*/
function pop() {
if (!isset($this->scopes[1]))
throw new Exception('cannnot pop from empty stack');
return array_shift($this->scopes);
}
function offsetExists($offset) {
foreach ($this->scopes as $layer) {
if (isset($layer[$offset])) return true;
}
return false;
}
function offsetGet($key) {
foreach ($this->scopes as $layer) {
if (isset($layer[$key]))
return $layer[$key];
}
return;
}
function offsetSet($key, $value) {
if (strpos($key, '.') > -1)
throw new Exception('cannot set non local variable');
return $this->scopes[0][$key] = $value;
}
function offsetUnset($key) {
foreach ($this->scopes as $layer) {
if (isset($layer[$key])) unset($layer[$key]);
}
}
function extend($context) {
$this->scopes[0] = array_merge($this->scopes[0], $context);
}
function set($key, $value) {
return $this->offsetSet($key, $value);
}
function get($key) {
return $this->offsetGet($key);
}
function isDefined($key) {
return $this->offsetExists($key);
}
/**
*
*
*
* Variable name
*
* @param $var variable name or array(0 => variable name, 'filters' => filters array)
* @return unknown_type
*/
function resolve($var) {
# if $var is array - it contains filters to apply
$filters = array();
if ( is_array($var) ) {
$name = array_shift($var);
$filters = isset($var['filters'])? $var['filters'] : array();
}
else $name = $var;
$result = null;
# Lookup basic types, null, boolean, numeric and string
# Variable starts with : (:users.name) to short-circuit lookup
if ($name[0] === ':') {
$object = $this->getVariable(substr($name, 1));
if (!is_null($object)) $result = $object;
} else {
if ($name === 'true') {
$result = true;
}
elseif ($name === 'false') {
$result = false;
}
elseif (preg_match('/^-?\d+(\.\d+)?$/', $name, $matches)) {
$result = isset($matches[1])? floatval($name) : intval($name);
}
elseif (preg_match('/^"([^"\\\\]*(?:\\.[^"\\\\]*)*)"|' .
'\'([^\'\\\\]*(?:\\.[^\'\\\\]*)*)\'$/', $name)) {
$result = stripcslashes(substr($name, 1, -1));
}
}
if (!empty(self::$lookupTable) && $result == Null) {
$result = $this->externalLookup($name);
}
$result = $this->applyFilters($result,$filters);
return $result;
}
function getVariable($name) {
# Local variables. this gives as a bit of performance improvement
if (!strpos($name, '.'))
return $this[$name];
# Prepare for Big lookup
$parts = explode('.', $name);
$object = $this[array_shift($parts)];
# Lookup context
foreach ($parts as $part) {
if (is_array($object) or $object instanceof ArrayAccess) {
if (isset($object[$part])) {
$object = $object[$part];
} elseif ($part === 'first') {
$object = isset($object[0])?$object[0]:null;
} elseif ($part === 'last') {
$last = count($object)-1;
$object = isset($object[$last])?$object[$last]:null;
} elseif ($part === 'size' or $part === 'length') {
return count($object);
} else {
return null;
}
}
elseif (is_object($object)) {
if (isset($object->$part))
$object = $object->$part;
elseif (is_callable(array($object, $part))) {
$methodAllowed = in_array(get_class($object), $this->safeClass) ||
(isset($object->h2o_safe) && (
$object->h2o_safe === true || in_array($part, $object->h2o_safe)
)
);
$object = $methodAllowed ? $object->$part() : null;
}
else return null;
}
else return null;
}
return $object;
}
function applyFilters($object, $filters) {
foreach ($filters as $filter) {
$name = substr(array_shift($filter), 1);
$args = $filter;
if (isset(h2o::$filters[$name])) {
foreach ($args as $i => $argument) {
# name args
if (is_array($argument)) {
foreach ($argument as $n => $arg) {
$args[$i][$n] = $this->resolve($arg);
}
} else {
# resolve argument values
$args[$i] = $this->resolve($argument);
}
}
array_unshift($args, $object);
$object = call_user_func_array(h2o::$filters[$name], $args);
}
}
return $object;
}
function escape($value, $var) {
$safe = false;
$filters = (is_array($var) && isset($var['filters']))? $var['filters'] : array();
foreach ( $filters as $filter ) {
$name = substr(array_shift($filter), 1);
$safe = !$safe && ($name === 'safe');
$escaped = $name === 'escape';
}
$should_escape = $this->autoescape || isset($escaped) && $escaped;
if ( ($should_escape && !$safe)) {
$value = htmlspecialchars($value);
}
return $value;
}
function externalLookup($name) {
if (!empty(self::$lookupTable)) {
foreach (self::$lookupTable as $lookup) {
$tmp = call_user_func_array($lookup, array($name, $this));
if ($tmp !== null)
return $tmp;
}
}
return null;
}
}
class BlockContext {
var $h2o_safe = array('name', 'depth', 'super');
var $block, $index;
private $context;
function __construct($block, $context, $index) {
$this->block =& $block;
$this->context = $context;
$this->index = $index;
}
function name() {
return $this->block->name;
}
function depth() {
return $this->index;
}
function super() {
$stream = new StreamWriter;
$this->block->parent->render($this->context, $stream, $this->index+1);
return $stream->close();
}
function __toString() {
return "[BlockContext : {$this->block->name}, {$this->block->filename}]";
}
}
?>

173
system/vendor/h2o/datatype.php vendored 100644
View File

@ -0,0 +1,173 @@
<?php
class StreamWriter {
var $buffer = array();
var $close;
function __construct() {
$this->close = false;
}
function write($data) {
if ($this->close)
new Exception('tried to write to closed stream');
$this->buffer[] = $data;
}
function close() {
$this->close = true;
return implode('', $this->buffer);
}
}
class Evaluator {
static function gt($l, $r) { return $l > $r; }
static function ge($l, $r) { return $l >= $r; }
static function lt($l, $r) { return $l < $r; }
static function le($l, $r) { return $l <= $r; }
static function eq($l, $r) { return $l == $r; }
static function ne($l, $r) { return $l != $r; }
static function not_($bool) { return !$bool; }
static function and_($l, $r) { return ($l && $r); }
static function or_($l, $r) { return ($l && $r); }
# Currently only support single expression with no preceddence ,no boolean expression
# [expression] = [optional binary] ? operant [ optional compare operant]
# [operant] = variable|string|numeric|boolean
# [compare] = > | < | == | >= | <=
# [binary] = not | !
static function exec($args, $context) {
$argc = count($args);
$first = array_shift($args);
$first = $context->resolve($first);
switch ($argc) {
case 1 :
return $first;
case 2 :
if (is_array($first) && isset($first['operator']) && $first['operator'] == 'not') {
$operant = array_shift($args);
$operant = $context->resolve($operant);
return !($operant);
}
case 3 :
list($op, $right) = $args;
$right = $context->resolve($right);
return call_user_func(array("Evaluator", $op['operator']), $first, $right);
default:
return false;
}
}
}
/**
* $type of token, Block | Variable
*/
class H2o_Token {
function __construct ($type, $content, $position) {
$this->type = $type;
$this->content = $content;
$this->result='';
$this->position = $position;
}
function write($content){
$this->result= $content;
}
}
/**
* a token stream
*/
class TokenStream {
var $pushed;
var $stream;
var $closed;
var $c;
function __construct() {
$this->pushed = array();
$this->stream = array();
$this->closed = false;
}
function pop() {
if (count($this->pushed))
return array_pop($this->pushed);
return array_pop($this->stream);
}
function feed($type, $contents, $position) {
if ($this->closed)
throw new Exception('cannot feed closed stream');
$this->stream[] = new H2o_Token($type, $contents, $position);
}
function push($token) {
if (is_null($token))
throw new Exception('cannot push NULL');
if ($this->closed)
$this->pushed[] = $token;
else
$this->stream[] = $token;
}
function close() {
if ($this->closed)
new Exception('cannot close already closed stream');
$this->closed = true;
$this->stream = array_reverse($this->stream);
}
function isClosed() {
return $this->closed;
}
function current() {
return $this->c ;
}
function next() {
return $this->c = $this->pop();
}
}
class H2o_Info {
var $h2o_safe = array('filters', 'extensions', 'tags');
var $name = 'H2o Template engine';
var $description = "Django inspired template system";
var $version = H2O_VERSION;
function filters() {
return array_keys(h2o::$filters);
}
function tags() {
return array_keys(h2o::$tags);
}
function extensions() {
return array_keys(h2o::$extensions);
}
}
/**
* Functions
*/
function sym_to_str($string) {
return substr($string, 1);
}
function is_sym($string) {
return isset($string[0]) && $string[0] === ':';
}
function symbol($string) {
return ':'.$string;
}
function strip_regex($regex, $delimiter = '/') {
return substr($regex, 1, strrpos($regex, $delimiter)-1);
}
?>

10
system/vendor/h2o/errors.php vendored 100644
View File

@ -0,0 +1,10 @@
<?php
# Errors
class H2o_Error extends Exception {}
class ParseError extends H2o_Error {}
class TemplateNotFound extends H2o_Error {}
class TemplateSyntaxError extends H2o_Error {}
?>

369
system/vendor/h2o/filters.php vendored 100644
View File

@ -0,0 +1,369 @@
<?php
class FilterCollection {};
class CoreFilters extends FilterCollection {
static function first($value) {
return $value[0];
}
static function last($value) {
return $value[count($value) - 1];
}
static function join($value, $delimiter = ', ') {
return join($delimiter, $value);
}
static function length($value) {
return count($value);
}
static function urlencode($data) {
if (is_array($data)) {
$result;
foreach ($data as $name => $value) {
$result .= $name.'='.urlencode($value).'&'.$querystring;
}
$querystring = substr($result, 0, strlen($result)-1);
return htmlspecialchars($result);
} else {
return urlencode($data);
}
}
static function hyphenize ($string) {
$rules = array('/[^\w\s-]+/'=>'','/\s+/'=>'-', '/-{2,}/'=>'-');
$string = preg_replace(array_keys($rules), $rules, trim($string));
return $string = trim(strtolower($string));
}
static function urlize( $string, $truncate = false ) {
$reg_exUrl = "/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/";
preg_match_all($reg_exUrl, $string, $matches);
$usedPatterns = array();
foreach($matches[0] as $pattern){
if(!array_key_exists($pattern, $usedPatterns)){
$usedPatterns[$pattern]=true;
$string = str_replace($pattern, "<a href=\"{$pattern}\" rel=\"nofollow\">{$pattern}</a>", $string);
}
}
$reg_exEmail = "/[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}/";
preg_match_all($reg_exEmail, $string, $matches);
$usedPatterns = array();
foreach($matches[0] as $pattern){
if(!array_key_exists($pattern, $usedPatterns)){
$usedPatterns[$pattern]=true;
$string = str_replace($pattern, "<a href=\"mailto:{$pattern}\">{$pattern}</a>", $string);
}
}
return $string;
}
static function set_default($object, $default) {
return !$object ? $default : $object;
}
}
class StringFilters extends FilterCollection {
static function humanize($string) {
$string = preg_replace('/\s+/', ' ', trim(preg_replace('/[^A-Za-z0-9()!,?$]+/', ' ', $string)));
return capfirst($string);
}
static function capitalize($string) {
return ucwords(strtolower($string)) ;
}
static function titlize($string) {
return self::capitalize($string);
}
static function capfirst($string) {
$string = strtolower($string);
return strtoupper($string{0}). substr($string, 1, strlen($string));
}
static function tighten_space($value) {
return preg_replace("/\s{2,}/", ' ', $value);
}
static function escape($value, $attribute = false) {
return htmlspecialchars($value, $attribute ? ENT_QUOTES : ENT_NOQUOTES);
}
static function escapejson($value) {
// The standard django escapejs converts all non-ascii characters into hex codes.
// This function encodes the entire data structure, and strings get quotes around them.
return json_encode($value);
}
static function force_escape($value, $attribute = false) {
return self::escape($value, $attribute);
}
static function e($value, $attribute = false) {
return self::escape($value, $attribute);
}
static function safe($value) {
return $value;
}
static function truncate ($string, $max = 50, $ends = '...') {
return (strlen($string) > $max ? substr($string, 0, $max).$ends : $string);
}
static function limitwords($text, $limit = 50, $ends = '...') {
if (strlen($text) > $limit) {
$words = str_word_count($text, 2);
$pos = array_keys($words);
if (isset($pos[$limit])) {
$text = substr($text, 0, $pos[$limit]) . $ends;
}
}
return $text;
}
}
class NumberFilters extends FilterCollection {
static function filesize ($bytes, $round = 1) {
if ($bytes === 0)
return '0 bytes';
elseif ($bytes === 1)
return '1 byte';
$units = array(
'bytes' => pow(2, 0), 'kB' => pow(2, 10),
'BM' => pow(2, 20), 'GB' => pow(2, 30),
'TB' => pow(2, 40), 'PB' => pow(2, 50),
'EB' => pow(2, 60), 'ZB' => pow(2, 70)
);
$lastUnit = 'bytes';
foreach ($units as $unitName => $unitFactor) {
if ($bytes >= $unitFactor) {
$lastUnit = $unitName;
} else {
$number = round( $bytes / $units[$lastUnit], $round );
return number_format($number) . ' ' . $lastUnit;
}
}
}
static function currency($amount, $currency = 'USD', $precision = 2, $negateWithParentheses = false) {
$definition = array(
'EUR' => array('<27>','.',','), 'GBP' => '<27>', 'JPY' => '<27>',
'USD'=>'$', 'AU' => '$', 'CAN' => '$'
);
$negative = false;
$separator = ',';
$decimals = '.';
$currency = strtoupper($currency);
// Is negative
if (strpos('-', $amount) !== false) {
$negative = true;
$amount = str_replace("-","",$amount);
}
$amount = (float) $amount;
if (!$negative) {
$negative = $amount < 0;
}
if ($negateWithParentheses) {
$amount = abs($amount);
}
// Get rid of negative zero
$zero = round(0, $precision);
if (round($amount, $precision) === $zero) {
$amount = $zero;
}
if (isset($definition[$currency])) {
$symbol = $definition[$currency];
if (is_array($symbol))
@list($symbol, $separator, $decimals) = $symbol;
} else {
$symbol = $currency;
}
$amount = number_format($amount, $precision, $decimals, $separator);
return $negateWithParentheses ? "({$symbol}{$amount})" : "{$symbol}{$amount}";
}
}
class HtmlFilters extends FilterCollection {
static function base_url($url, $options = array()) {
return $url;
}
static function asset_url($url, $options = array()) {
return self::base_url($url, $options);
}
static function image_tag($url, $options = array()) {
$attr = self::htmlAttribute(array('alt','width','height','border'), $options);
return sprintf('<img src="%s" %s/>', $url, $attr);
}
static function css_tag($url, $options = array()) {
$attr = self::htmlAttribute(array('media'), $options);
return sprintf('<link rel="stylesheet" href="%s" type="text/css" %s />', $url, $attr);
}
static function script_tag($url, $options = array()) {
return sprintf('<script src="%s" type="text/javascript"></script>', $url);
}
static function links_to($text, $url, $options = array()) {
$attrs = self::htmlAttribute(array('ref'), $options);
$url = self::base_url($url, $options);
return sprintf('<a href="%s" %s>%s</a>', $url, $attrs, $text);
}
static function links_with ($url, $text, $options = array()) {
return self::links_to($text, $url, $options);
}
static function strip_tags($text) {
$text = preg_replace(array('/</', '/>/'), array(' <', '> '),$text);
return strip_tags($text);
}
static function linebreaks($value, $format = 'p') {
if ($format === 'br')
return HtmlFilters::nl2br($value);
return HtmlFilters::nl2pbr($value);
}
static function nl2br($value) {
return str_replace("\n", "<br />\n", $value);
}
static function nl2pbr($value) {
$result = array();
$parts = preg_split('/(\r?\n){2,}/m', $value);
foreach ($parts as $part) {
array_push($result, '<p>' . HtmlFilters::nl2br($part) . '</p>');
}
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'));
?>

290
system/vendor/h2o/loaders.php vendored 100644
View File

@ -0,0 +1,290 @@
<?php
/**
*
* @author taylor.luk
* @todo FileLoader need more test coverage
*/
class H2o_Loader {
public $parser;
public $runtime;
public $cached = false;
protected $cache = false;
public $searchpath = false;
function read($filename) {}
function cache_read($file, $object, $ttl = 3600) {}
}
class H2o_File_Loader extends H2o_Loader {
function __construct($searchpath, $options = array()) {
// if (is_file($searchpath)) {
// $searthpath = dirname($searchpath).DS;
// }
// if (!is_dir($searchpath))
// throw new TemplateNotFound($filename);
//
if (!is_array($searchpath))
throw new Exception("searchpath must be an array");
$this->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 );
}
}

84
system/vendor/h2o/nodes.php vendored 100644
View File

@ -0,0 +1,84 @@
<?php
/*
Nodes
*/
class H2o_Node {
var $position;
function __construct($argstring) {}
function render($context, $stream) {}
}
class NodeList extends H2o_Node implements IteratorAggregate {
var $parser;
var $list;
function __construct(&$parser, $initial = null, $position = 0) {
$this->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));
}
}
?>

290
system/vendor/h2o/parser.php vendored 100644
View File

@ -0,0 +1,290 @@
<?php
class H2o_Lexer {
function __construct($options = array()) {
$this->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;
}
}
?>

483
system/vendor/h2o/tags.php vendored 100644
View File

@ -0,0 +1,483 @@
<?php
/**
*
* @author taylor.luk
* @todo tags need more test coverage
*/
/**
* ifchanged tag
*
* Usage:
*
* Variable mode
* {% ifchanged data.date %}...{% endifchanged %}
*
* Lazy mode *not implemented in h2o yet
* {% ifchanged %}...{{ data.date }}...{% endifchanged %}
*
*/
class IfChanged_Tag extends H2o_Node {
private $nodelist_true;
private $nodelist_false;
private $_varlist = null;
private $_last_seen = null;
function __construct($argstring, $parser, $position = 0) {
$this->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 = "<pre>" . htmlspecialchars( print_r($object, true) ) . "</pre>";
$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("<div style='display:none'><input type=\"hidden\" value=\"$token\" name=\"csrfmiddlewaretoken\" /></div>");
}
}
H2o::addTag(array('block', 'extends', 'include', 'if', 'ifchanged', 'for', 'with', 'cycle', 'load', 'debug', 'comment', 'now', 'autoescape', 'csrf_token'));