Adding template engine
Adding error handling Adding basic SMTP class core routing is working
This commit is contained in:
parent
703a7d5bbd
commit
a7c962462d
@ -1,2 +1,5 @@
|
|||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
RewriteRule ^(.*)$ /index.php/$1 [L]
|
RewriteBase /test
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteRule ^(.*)$ index.php/$1 [L]
|
1
application/.htaccess
Normal file
1
application/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
Deny from all
|
5
application/config.php
Normal file
5
application/config.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$config["ADMINS"] = array();
|
||||||
|
|
||||||
|
return $config;
|
1
application/controllers/.htaccess
Normal file
1
application/controllers/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
Deny from all
|
1
application/tmp/.htaccess
Normal file
1
application/tmp/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
Deny from all
|
1
application/views/.htaccess
Normal file
1
application/views/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
Deny from all
|
@ -1,10 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
require('system/engine/core.php');
|
|
||||||
|
|
||||||
foreach (glob("system/vendor/*.php") as $filename)
|
foreach (glob("system/vendor/*.php") as $filename)
|
||||||
{
|
{
|
||||||
include $filename;
|
include $filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
require('system/engine/core.php');
|
||||||
|
|
||||||
$core = new HF_Core();
|
$core = new HF_Core();
|
||||||
$core->run();
|
$core->run();
|
1
system/.htaccess
Normal file
1
system/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
Deny from all
|
1
system/engine/.htaccess
Normal file
1
system/engine/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
Deny from all
|
80
system/engine/SMTP.php
Normal file
80
system/engine/SMTP.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
14
system/engine/config-default.php
Normal file
14
system/engine/config-default.php
Normal 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;
|
15
system/engine/controller.php
Normal file
15
system/engine/controller.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,63 +1,235 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
include "system/engine/controller.php";
|
||||||
|
include "system/engine/SMTP.php";
|
||||||
|
include "system/engine/exceptions.php";
|
||||||
|
|
||||||
class HF_Core
|
class HF_Core
|
||||||
{
|
{
|
||||||
private $class;
|
private $class;
|
||||||
private $method;
|
private $method;
|
||||||
private $classname;
|
private $classname;
|
||||||
private $args = array();
|
private $args = array();
|
||||||
|
private $config = array();
|
||||||
|
private $tpl;
|
||||||
|
|
||||||
public function __construct()
|
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()
|
private function findController()
|
||||||
{
|
{
|
||||||
$request = $_SERVER["REQUEST_URI"];
|
try
|
||||||
if ($request == "" || $request == "/")
|
{
|
||||||
{
|
$request = $_SERVER["PHP_SELF"];
|
||||||
require("../../application/controllers/main.php");
|
$splitreq = explode("/", $request);
|
||||||
$this->$class = new main();
|
$request = "";
|
||||||
$this->$method = "index";
|
for($i = 0; $i < count($splitreq); $i++)
|
||||||
$this->$classname = "main";
|
{
|
||||||
return;
|
if ($splitreq[$i] == "index.php")
|
||||||
}
|
{
|
||||||
$arr = explode("/", $request);
|
$request = implode("/", array_splice($splitreq, $i+1));
|
||||||
$path = "../../application/controllers/";
|
}
|
||||||
for($i = 0; $i < count($arr); $i++)
|
}
|
||||||
{
|
|
||||||
if ($is_file($path . $arr[$i] . ".php")) // found the controller
|
if ($request == "" || $request == "/")
|
||||||
{
|
{
|
||||||
include($path . $arr[$i] . ".php");
|
require("application/controllers/" . $this->config["DEFAULT_ROUTE"] . ".php");
|
||||||
$this->$class = new $arr[$i];
|
$this->loadController(new $this->config["DEFAULT_ROUTE"]($this->config, $this, $this->tpl), $this->config["DEFAULT_ROUTE"], "index");
|
||||||
|
return;
|
||||||
if ($i + 1 != count($arr)) // if there is a define after the controller name - this would be the method name
|
}
|
||||||
{
|
if ($request[strlen($request)-1] == "/")
|
||||||
$this->$method = $arr[$i+1];
|
$request = substr($request, 0, -1);
|
||||||
$this->$args = array_slice ($arr, 2);
|
$arr = explode("/", $request);
|
||||||
$this->$classname = $arr[$i];
|
$path = "application/controllers/";
|
||||||
} else { // call index
|
for($i = 0; $i < count($arr); $i++)
|
||||||
$this->$method = "index";
|
{
|
||||||
$this->$classname = $arr[$i];
|
if (is_file($path . $arr[$i] . ".php")) // found the controller
|
||||||
}
|
{
|
||||||
return;
|
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
|
||||||
|
{
|
||||||
if (is_dir($path . $arr[$i])) // controller is hidden deeper
|
$this->loadController(new $arr[$i]($this->config, $this, $this->tpl), $arr[$i], $arr[$i+1], array_slice ($arr, 2));
|
||||||
{
|
} else { // call index
|
||||||
$path = $path . $arr[$i] . "/";
|
$this->loadController(new $arr[$i]($this->config, $this, $this->tpl), $arr[$i], "index");
|
||||||
continue;
|
}
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
// throw exception controller not found
|
|
||||||
}
|
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);
|
try
|
||||||
$call->invokeArgs($this->$class, $this->$args);
|
{
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
18
system/engine/exceptions.php
Normal file
18
system/engine/exceptions.php
Normal 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 {}
|
15
system/engine/status.php
Normal file
15
system/engine/status.php
Normal 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
Normal file
1
system/vendor/.htaccess
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Deny from all
|
453
system/vendor/README.md
vendored
Normal file
453
system/vendor/README.md
vendored
Normal 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.
|
66
system/vendor/StackTracePrint.php
vendored
Normal file
66
system/vendor/StackTracePrint.php
vendored
Normal 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/', ' → <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•$1</span> <b style='color: brown'>[]</b>", $str);
|
||||||
|
$str = preg_replace('/ string\((\d+)\) \"(.*)\"/', " <span style='color: #888'>str•$1</span> <b style='color: brown'>'$2'</b>", $str);
|
||||||
|
$str = preg_replace('/\[\"(.+)\"\] => /', "<span style='color: purple'>'$1'</span> → ", $str);
|
||||||
|
$str = preg_replace('/object\((\S+)\)#(\d+) \((\d+)\) {/', "<span style='color: #888'>obj•$2</span> <b style='color: #0C9136'>$1[$3]</b> {", $str);
|
||||||
|
$str = str_replace("bool(false)", "<span style='color:#888'>bool•</span><span style='color: red'>false</span>", $str);
|
||||||
|
$str = str_replace("bool(true)", "<span style='color:#888'>bool•</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
Normal file
266
system/vendor/h2o.php
vendored
Normal 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
Normal file
1
system/vendor/h2o/.htaccess
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Deny from all
|
266
system/vendor/h2o/context.php
vendored
Normal file
266
system/vendor/h2o/context.php
vendored
Normal 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
Normal file
173
system/vendor/h2o/datatype.php
vendored
Normal 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
Normal file
10
system/vendor/h2o/errors.php
vendored
Normal 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
Normal file
369
system/vendor/h2o/filters.php
vendored
Normal 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
Normal file
290
system/vendor/h2o/loaders.php
vendored
Normal 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
Normal file
84
system/vendor/h2o/nodes.php
vendored
Normal 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
Normal file
290
system/vendor/h2o/parser.php
vendored
Normal 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
Normal file
483
system/vendor/h2o/tags.php
vendored
Normal 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'));
|
Loading…
Reference in New Issue
Block a user