3
0

upd(codegen): generate subpackages

This commit is contained in:
Tit Petric
2018-07-16 00:13:45 +02:00
parent 657ef15cf5
commit b68337c111
62 changed files with 3705 additions and 38 deletions

View File

@@ -1,15 +1,34 @@
#!/usr/bin/env php
<?php
if (count($argv) < 2) {
echo "Usage: ./codegen [path]\n";
exit(255);
}
if (!is_dir(__DIR__ . "/" . $argv[1])) {
echo "No folder " . $argv[1] . "\n";
die;
}
$project = $argv[1];
error_reporting(E_ALL^E_NOTICE);
include("docs/vendor/autoload.php");
include(__DIR__ . "/vendor/autoload.php");
function capitalize($name) {
$names = explode("/", $name);
return implode("", array_map("ucfirst", $names));
}
function expose($name) {
if ($name == "id") {
return "ID";
}
return ucfirst($name);
}
function array_change_key_case_recursive($arr) {
return array_map(function ($item) {
if (is_array($item)) {
@@ -20,11 +39,13 @@ function array_change_key_case_recursive($arr) {
}
$tpl = new Monotek\MiniTPL\Template;
$tpl->set_paths(array(__DIR__ . "/templates/"));
$tpl->set_compile_location("/tmp", true);
$tpl->add_default("newline", "\n");
$api_files = glob("docs/src/spec/*.json");
$generators = array();
exec("find -L " . __DIR__ . "/" . $project . " -name index.php", $generators);
$api_files = glob($project . "/docs/src/spec/*.json");
$apis = array_map(function($filename) {
return array_change_key_case_recursive(json_decode(file_get_contents($filename), true));
}, $api_files);
@@ -33,17 +54,34 @@ usort($apis, function($a, $b) {
return strcmp($a['interface'], $b['interface']);
});
foreach (array("structs", "handlers", "interfaces", "request", "") as $type) {
$parsers = array(
"uint64" => "parseUInt64",
"bool" => "parseBool",
);
foreach ($generators as $generator) {
$tpl->set_paths(array(dirname($generator) . "/", __DIR__ . "/templates/"));
$dirname = strstr(dirname($generator), $project. "/");
if (empty($dirname)) {
$dirname = $project;
}
// echo "generator=". dirname($generator) . " project=$project, dirname=$dirname\n";
if (!is_dir($dirname) && !empty($dirname)) {
mkdir($dirname, 0777, true);
}
$common = compact("parsers");
include($generator);
}
/* foreach (array("structs", "handlers", "interfaces", "request", "") as $type) {
foreach ($apis as $api) {
if (is_array($api['struct'])) {
$name = ucfirst($api['interface']);
$filename = str_replace("..", ".", strtolower($name) . "." . $type . ".go");
$tpl->load("http_$type.tpl");
$tpl->assign("parsers", array(
"uint64" => "parseUInt64",
"bool" => "parseBool",
));
$tpl->assign("parsers",
$tpl->assign("package", $api['package']);
$tpl->assign("name", $name);
$tpl->assign("self", strtolower(substr($name, 0, 1)));
@@ -82,6 +120,7 @@ foreach (array("routes") as $type) {
file_put_contents($filename, $contents);
}
*/
// camel case to snake case
function decamel($input) {

94
codegen/codegen_old.php Executable file
View File

@@ -0,0 +1,94 @@
#!/usr/bin/env php
<?php
error_reporting(E_ALL^E_NOTICE);
include("docs/vendor/autoload.php");
function capitalize($name) {
$names = explode("/", $name);
return implode("", array_map("ucfirst", $names));
}
function array_change_key_case_recursive($arr) {
return array_map(function ($item) {
if (is_array($item)) {
$item = array_change_key_case_recursive($item);
}
return $item;
}, array_change_key_case($arr));
}
$tpl = new Monotek\MiniTPL\Template;
$tpl->set_paths(array(__DIR__ . "/"));
$tpl->set_compile_location("/tmp", true);
$tpl->add_default("newline", "\n");
$api_files = glob("docs/src/spec/*.json");
$apis = array_map(function($filename) {
return array_change_key_case_recursive(json_decode(file_get_contents($filename), true));
}, $api_files);
usort($apis, function($a, $b) {
return strcmp($a['interface'], $b['interface']);
});
foreach (array("structs", "handlers", "interfaces", "request", "") as $type) {
foreach ($apis as $api) {
if (is_array($api['struct'])) {
$name = ucfirst($api['interface']);
$filename = str_replace("..", ".", strtolower($name) . "." . $type . ".go");
$tpl->load("http_$type.tpl");
$tpl->assign("parsers", array(
"uint64" => "parseUInt64",
"bool" => "parseBool",
));
$tpl->assign("package", $api['package']);
$tpl->assign("name", $name);
$tpl->assign("self", strtolower(substr($name, 0, 1)));
$tpl->assign("api", $api);
$tpl->assign("structs", $api['struct']);
$imports = array();
foreach ($api['struct'] as $struct) {
if (isset($struct['imports']))
foreach ($struct['imports'] as $import) {
$imports[] = $import;
}
}
$tpl->assign("imports", $imports);
$tpl->assign("calls", $api['apis']);
$contents = str_replace("\n\n}", "\n}", $tpl->get());
$save = true;
if ($type === "" && file_exists($filename)) {
$save = false;
}
if ($save) {
file_put_contents($filename, $contents);
}
}
}
}
foreach (array("routes") as $type) {
$name = ucfirst($api['interface']);
$filename = str_replace("..", ".", $type . ".go");
$tpl->load("http_$type.tpl");
$tpl->assign("package", reset($apis)['package']);
$tpl->assign("apis", $apis);
$contents = $tpl->get();
file_put_contents($filename, $contents);
}
// camel case to snake case
function decamel($input) {
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
}
return implode('_', $ret);
}

5
codegen/composer.json Normal file
View File

@@ -0,0 +1,5 @@
{
"require": {
"monotek/minitpl": "^1.1"
}
}

67
codegen/composer.lock generated Normal file
View File

@@ -0,0 +1,67 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "27ca9291b11653ca55b2eb53454a2d60",
"content-hash": "789a4ed7b74ed2be14054128f82abcde",
"packages": [
{
"name": "monotek/minitpl",
"version": "v1.1.4",
"source": {
"type": "git",
"url": "https://github.com/titpetric/minitpl.git",
"reference": "613a596b3dee3c121283fad3f84d06d83ecb5125"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/titpetric/minitpl/zipball/613a596b3dee3c121283fad3f84d06d83ecb5125",
"reference": "613a596b3dee3c121283fad3f84d06d83ecb5125",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Monotek\\MiniTPL": "code"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"CC BY-SA 3.0"
],
"authors": [
{
"name": "Tit Petric",
"email": "tit.petric@monotek.net",
"homepage": "http://titpetric.github.io",
"role": "developer"
}
],
"description": "Miniature fully featured PHP template engine",
"homepage": "https://github.com/titpetric/minitpl",
"keywords": [
"minitpl",
"monotek",
"php",
"small",
"smarty",
"template",
"tiny"
],
"time": "2016-02-07 13:23:38"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

56
codegen/crm/index.php Normal file
View File

@@ -0,0 +1,56 @@
<?php
$name = ucfirst($api['interface']);
$filename = $dirname . "/rest_router.go";
$tpl->load("http_routes.tpl");
$tpl->assign($common);
$tpl->assign("package", basename($dirname));
$tpl->assign("name", $name);
$tpl->assign("api", $api);
$tpl->assign("apis", $apis);
$tpl->assign("self", strtolower(substr($name, 0, 1)));
$tpl->assign("structs", $api['struct']);
$imports = array();
foreach ($api['struct'] as $struct) {
if (isset($struct['imports']))
foreach ($struct['imports'] as $import) {
$imports[] = $import;
}
}
$tpl->assign("imports", $imports);
$tpl->assign("calls", $api['apis']);
$contents = str_replace("\n\n}", "\n}", $tpl->get());
file_put_contents($filename, $contents);
echo $filename . "\n";
foreach ($apis as $api) {
if (is_array($api['struct'])) {
$name = ucfirst($api['interface']);
$filename = $dirname . "/" . str_replace("..", ".", strtolower($name) . ".go");
$tpl->load("http_.tpl");
$tpl->assign($common);
$tpl->assign("package", basename(__DIR__));
$tpl->assign("name", $name);
$tpl->assign("api", $api);
$tpl->assign("self", strtolower(substr($name, 0, 1)));
$tpl->assign("structs", $api['struct']);
$imports = array();
foreach ($api['struct'] as $struct) {
if (isset($struct['imports']))
foreach ($struct['imports'] as $import) {
$imports[] = $import;
}
}
$tpl->assign("imports", $imports);
$tpl->assign("calls", $api['apis']);
$contents = str_replace("\n\n}", "\n}", $tpl->get());
if (!file_exists($filename)) {
file_put_contents($filename, $contents);
echo $filename . "\n";
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
$templates = array(
"http_interfaces.tpl" => function($name, $api) {
return strtolower($name) . ".go";
},
"http_request.tpl" => function($name, $api) {
return strtolower($name) . "_requests.go";
},
"http_handlers.tpl" => function($name, $api) {
return strtolower($name) . "_handlers.go";
}
);
foreach ($templates as $template => $fn)
foreach ($apis as $api) {
if (is_array($api['struct'])) {
$name = ucfirst($api['interface']);
$filename = $dirname . "/" . $fn($name, $api);
$tpl->load($template);
$tpl->assign($common);
$tpl->assign("package", basename(__DIR__));
$tpl->assign("name", $name);
$tpl->assign("api", $api);
$tpl->assign("self", strtolower(substr($name, 0, 1)));
$tpl->assign("structs", $api['struct']);
$imports = array();
foreach ($api['struct'] as $struct) {
if (isset($struct['imports']))
foreach ($struct['imports'] as $import) {
$imports[] = $import;
}
}
$tpl->assign("imports", $imports);
$tpl->assign("calls", $api['apis']);
$contents = str_replace("\n\n}", "\n}", $tpl->get());
file_put_contents($filename, $contents);
echo $filename . "\n";
}
}

View File

@@ -0,0 +1,29 @@
<?php
foreach ($apis as $api) {
if (is_array($api['struct'])) {
$name = ucfirst($api['interface']);
$filename = $dirname . "/" . str_replace("..", ".", strtolower($name) . ".go");
$tpl->load("http_structs.tpl");
$tpl->assign($common);
$tpl->assign("package", basename(__DIR__));
$tpl->assign("name", $name);
$tpl->assign("api", $api);
$tpl->assign("self", strtolower(substr($name, 0, 1)));
$tpl->assign("structs", $api['struct']);
$imports = array();
foreach ($api['struct'] as $struct) {
if (isset($struct['imports']))
foreach ($struct['imports'] as $import) {
$imports[] = $import;
}
}
$tpl->assign("imports", $imports);
$tpl->assign("calls", $api['apis']);
$contents = str_replace("\n\n}", "\n}", $tpl->get());
file_put_contents($filename, $contents);
echo $filename . "\n";
}
}

1
codegen/sam Symbolic link
View File

@@ -0,0 +1 @@
crm

View File

@@ -6,6 +6,12 @@ import (
var _ = errors.Wrap
type {name} struct {}
func ({name}) New() *{name} {
return &{name}{ldelim}{rdelim}
}
{foreach $calls as $call}
func (*{name}) {call.name|capitalize}(r *{name|lcfirst}{call.name|capitalize}Request) (interface{}, error) {
return nil, errors.New("Not implemented: {name}.{call.name}")

View File

@@ -10,7 +10,7 @@ import (
{foreach $calls as $call}
func ({self}h *{name}Handlers) {call.name|capitalize}(w http.ResponseWriter, r *http.Request) {
params := {name|lcfirst}{call.name|capitalize}Request{}.new()
params := {name|capitalize}{call.name|capitalize}Request{}.new()
resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return {self}h.{name}.{call.name|capitalize}(params) })
}
{/foreach}

View File

@@ -8,20 +8,17 @@ import (
// HTTP handlers are a superset of internal APIs
type {name}Handlers struct {
*{name}
}
func ({name}Handlers) new() *{name}Handlers {
return &{name}Handlers{
{name}{}.New(),
}
{name} {name}API
}
// Internal API interface
type {name}API interface {
{foreach $calls as $call}
{call.name|capitalize}(*{name|lcfirst}{call.name|capitalize}Request) (interface{}, error)
{call.name|capitalize}(*{name|expose}{call.name|capitalize}Request) (interface{}, error)
{/foreach}
// Authenticate API requests
Authenticator() func(http.Handler) http.Handler
}
// HTTP API interface
@@ -29,11 +26,4 @@ type {name}HandlersAPI interface {
{foreach $calls as $call}
{call.name|capitalize}(http.ResponseWriter, *http.Request)
{/foreach}
// Authenticate API requests
Authenticator() func(http.Handler) http.Handler
}
// Compile time check to see if we implement the interfaces
var _ {name}HandlersAPI = &{name}Handlers{}
var _ {name}API = &{name}{}

View File

@@ -11,19 +11,19 @@ var _ = chi.URLParam
{foreach $calls as $call}
// {name} {call.name} request parameters
type {name|lcfirst}{call.name|capitalize}Request struct {
type {name|expose}{call.name|capitalize}Request struct {
{foreach $call.parameters as $params}
{foreach $params as $method => $param}
{param.name} {param.type}{newline}
{param.name|expose} {param.type}{newline}
{/foreach}
{/foreach}
}
func ({name|lcfirst}{call.name|capitalize}Request) new() *{name|lcfirst}{call.name|capitalize}Request {
return &{name|lcfirst}{call.name|capitalize}Request{}
func ({name|expose}{call.name|capitalize}Request) new() *{name|expose}{call.name|capitalize}Request {
return &{name|expose}{call.name|capitalize}Request{}
}
func ({self} *{name|lcfirst}{call.name|capitalize}Request) Fill(r *http.Request) error {
func ({self} *{name|expose}{call.name|capitalize}Request) Fill(r *http.Request) error {
r.ParseForm()
get := map[string]string{}
post := map[string]string{}
@@ -38,14 +38,14 @@ func ({self} *{name|lcfirst}{call.name|capitalize}Request) Fill(r *http.Request)
{foreach $call.parameters as $method => $params}
{foreach $params as $param}
{if strtolower($method) === "path"}
{self}.{param.name} = {if ($param.type !== "string")}{$parsers[$param.type]}({/if}chi.URLParam(r, "{param.name}"){if ($param.type !== "string")}){/if}{newline}
{self}.{param.name|expose} = {if ($param.type !== "string")}{$parsers[$param.type]}({/if}chi.URLParam(r, "{param.name}"){if ($param.type !== "string")}){/if}{newline}
{elseif substr($param.type, 0, 2) !== '[]'}
{self}.{param.name} = {if ($param.type !== "string")}{$parsers[$param.type]}({method|strtolower}["{param.name}"]){else}{method|strtolower}["{param.name}"]{/if}{newline}
{self}.{param.name|expose} = {if ($param.type !== "string")}{$parsers[$param.type]}({method|strtolower}["{param.name}"]){else}{method|strtolower}["{param.name}"]{/if}{newline}
{/if}
{/foreach}
{/foreach}
return nil
}
var _ RequestFiller = {name|lcfirst}{call.name|capitalize}Request{}.new()
var _ RequestFiller = {name|expose}{call.name|capitalize}Request{}.new()
{/foreach}

View File

@@ -7,15 +7,17 @@ import (
"runtime"
"reflect"
"github.com/go-chi/chi"
"github.com/crusttech/crust/{package}/rest"
)
func MountRoutes(r chi.Router) {
{foreach $apis as $api}
{api.interface|strtolower} := {api.interface|capitalize}Handlers{}.new()
{api.interface|strtolower} := &rest.{api.interface|capitalize}Handlers{{api.interface|capitalize}{ldelim}{rdelim}.New()}
{/foreach}
{foreach $apis as $api}
r.Group(func (r chi.Router) {
r.Use({api.interface|strtolower}.Authenticator())
r.Use({api.interface|strtolower}.{api.interface}.Authenticator())
r.Route("{api.path}", func(r chi.Router) {
{foreach $api.apis as $call}
r.{eval echo capitalize(strtolower($call.method))}("{call.path}", {api.interface|strtolower}.{call.name|capitalize})

7
codegen/vendor/autoload.php vendored Normal file
View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInita6e366c180070d1defcfe90f77724cd9::getLoader();

413
codegen/vendor/composer/ClassLoader.php vendored Normal file
View File

@@ -0,0 +1,413 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative) {
return false;
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if ($file === null) {
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

21
codegen/vendor/composer/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
Copyright (c) 2016 Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,10 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Monotek\\MiniTPL' => array($vendorDir . '/monotek/minitpl/code'),
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,52 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInita6e366c180070d1defcfe90f77724cd9
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInita6e366c180070d1defcfe90f77724cd9', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInita6e366c180070d1defcfe90f77724cd9', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInita6e366c180070d1defcfe90f77724cd9::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
return $loader;
}
}

View File

@@ -0,0 +1,26 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInita6e366c180070d1defcfe90f77724cd9
{
public static $prefixesPsr0 = array (
'M' =>
array (
'Monotek\\MiniTPL' =>
array (
0 => __DIR__ . '/..' . '/monotek/minitpl/code',
),
),
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixesPsr0 = ComposerStaticInita6e366c180070d1defcfe90f77724cd9::$prefixesPsr0;
}, null, ClassLoader::class);
}
}

52
codegen/vendor/composer/installed.json vendored Normal file
View File

@@ -0,0 +1,52 @@
[
{
"name": "monotek/minitpl",
"version": "v1.1.4",
"version_normalized": "1.1.4.0",
"source": {
"type": "git",
"url": "https://github.com/titpetric/minitpl.git",
"reference": "613a596b3dee3c121283fad3f84d06d83ecb5125"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/titpetric/minitpl/zipball/613a596b3dee3c121283fad3f84d06d83ecb5125",
"reference": "613a596b3dee3c121283fad3f84d06d83ecb5125",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2016-02-07 13:23:38",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Monotek\\MiniTPL": "code"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"CC BY-SA 3.0"
],
"authors": [
{
"name": "Tit Petric",
"email": "tit.petric@monotek.net",
"homepage": "http://titpetric.github.io",
"role": "developer"
}
],
"description": "Miniature fully featured PHP template engine",
"homepage": "https://github.com/titpetric/minitpl",
"keywords": [
"minitpl",
"monotek",
"php",
"small",
"smarty",
"template",
"tiny"
]
}
]

View File

@@ -0,0 +1,2 @@
test/compile
vendor

View File

@@ -0,0 +1,3 @@
[submodule "git_hooks"]
path = git_hooks
url = git@github.com:titpetric/git_hooks.git

View File

@@ -0,0 +1,37 @@
# MiniTPL
The goal of the MiniTPL template engine is to provide a miniature
framework which allows you to rapidly create and consume
Smarty-like templates without adding the overhead of Smarty to
your choice of a PHP framework.
In benchmarks the speed of Mini TPL is very close to PHP itself.
All that is usually needed for Mini TPL is a 3KB PHP code overhead.
So it beats Smarty, and usual PHP vsprintf and str_replace functionality.
With a total size of about 13KB and the functionality contained, this is
one of the smallest full featured template engines for PHP to date.
MiniTPL is available on [packagist as monotek/minitpl](https://packagist.org/packages/monotek/minitpl).
To start using MiniTPL in your project with [composer](http://getcomposer.org/), create a composer.json file:
```
{
"require": {
"monotek/minitpl": ">=1.0"
}
}
```
And run `composer install`. You can start using MiniTPL right away
```
<?php
include("vendor/autoload.php");
$tpl = new Monotek\MiniTPL\Template;
$tpl->load("test.tpl");
$tpl->render();
```

View File

@@ -0,0 +1,345 @@
<?php
namespace Monotek\MiniTPL;
/*
Tit Petrič, Monotek d.o.o., (cc) tit.petric@monotek.net
http://creativecommons.org/licenses/by-sa/3.0/
*/
/** Template compiler class */
class Compiler
{
function __construct()
{
$this->_tag_php_open = "<"."?php";
$this->_tag_php_close = "?".">\n";
$this->_global_variables = array();
$this->_literals = array();
}
protected function load_contents($filename)
{
if (($contents = file_get_contents($filename)) !== false) {
if (substr($contents, 0, 3) == "\xEF\xBB\xBF") {
return substr($contents, 3);
}
}
return $contents;
}
/** Compile template file into php code */
function compile($filename, $output_filename, $find_path, $nocache)
{
$contents = $this->load_contents($filename);
$r = 0;
if ($contents!==false && $contents!=="") {
while (preg_match_all("/\{include\ (.*?)\}/s", $contents, $matches)) {
$matches = array_unique($matches[1]);
foreach ($matches as $file) {
$cn = "<!-- ".$file." -->";
if (($fn = call_user_func($find_path, $file))!==false) {
$cn = $this->load_contents($fn.$file);
}
$contents = str_replace("{include ".$file."}", $cn, $contents);
}
}
while (preg_match_all("/\{load\ (.*?)\}/s", $contents, $matches)) {
$matches = array_unique($matches[1]);
foreach ($matches as $file) {
$file_var = (substr($file,0,1) == '$') ? $this->_split_exp($file) : '"'.$file.'"';
$cn = $this->_code('$this->push();$this->load('.$file_var.');$this->assign($_v);$this->render();$this->pop();');
$contents = str_replace("{load ".$file."}", $cn, $contents);
}
}
$nocache = $nocache ? $this->_code("@unlink(__FILE__);") : "";
$contents = str_replace("{*nocache*}",$nocache,$contents);
$contents = $this->_strip_comments($contents);
$contents = $this->_parse_constants($contents);
$contents = $this->_parse_functions($contents, $filename);
$contents = $this->_parse_expressions($contents);
$contents = $this->_parse_variables($contents);
if (!empty($this->_global_variables)) {
$globals = array_unique($this->_global_variables);
$contents = $this->_code('global '.implode(", ",$globals).';').$contents;
}
$contents = $this->_template_cleanup($contents);
$this->_r_mkdir(dirname($output_filename));
if ($f = @fopen($output_filename,"w")) {
fwrite($f, $contents);
fclose($f);
$r = 1;
}
}
return $r;
}
function _r_mkdir($dir)
{
if (file_exists($dir)) return;
if (!file_exists(dirname($dir))) $this->_r_mkdir(dirname($dir));
@mkdir($dir);
}
/** Insert system configuration, clean up code */
function _template_cleanup($contents)
{
// set up variables
$contents = $this->_code('$_v=&$this->vars;') . $contents;
// strip unnecessary php tags
$contents = str_replace($this->_tag_php_close.$this->_tag_php_open, "", $contents);
// strip new line whitespace between php code
$contents = str_replace($this->_tag_php_close."\n".$this->_tag_php_open.' ', "", $contents);
$contents = str_replace("echo ;", "", $contents);
foreach ($this->_literals as $key=>$value) {
$contents = str_replace("[[".$key."]]", $value, $contents);
}
return $contents;
}
/** Strip template style comments */
function _strip_comments($contents)
{
$contents = preg_replace("/\{\*.+\*\}/sU", "", $contents);
return $contents;
}
/** Replace constant definitions */
function _parse_constants($contents)
{
$matches = array();
if (preg_match_all("/\{(\_[a-zA-Z0-9\_]+)\}/", $contents, $matches)) {
$matches = array_unique($matches[1]);
foreach ($matches as $m) {
$contents = str_replace("{".$m."}", $this->_code("echo ".$m.";"), $contents);
}
}
return $contents;
}
/** Search and replace for function blocks and inline definitions */
function _parse_functions($contents, $filename)
{
$inlines = $blocks = array();
if (preg_match_all("/\{(block|inline)\ ([a-zA-Z0-9\_\-]+)\}(.*?)\{\/\\1\}/s", $contents, $matches)) {
foreach ($matches[0] as $k=>$ma) {
$m = array("content"=>trim($matches[3][$k]), "src"=>$ma);
if ($matches[1][$k]=="block") {
$blocks[$matches[2][$k]] = $m;
} else {
$inlines[$matches[2][$k]] = $m;
}
}
}
if (preg_match_all("/\<script\ ([^\>]+)\>(.*?)\<\/script\>/s", $contents, $matches)) {
foreach ($matches[0] as $k=>$parameters) {
if (strpos($parameters,"text/template")!==false || strpos($parameters,"text/x-jquery")!==false) {
$key = count($this->_literals)."_literal";
$this->_literals[$key] = $matches[2][$k];
$contents = str_replace($matches[2][$k], "[[".$key."]]", $contents);
}
}
}
foreach ($blocks as $name=>$code) {
$lambda = sprintf("%u", crc32($code['content']))."_".sprintf("%u", crc32($filename));
$block_code = "if (!function_exists('".$name."_".$lambda."')) { function ".$name."_".$lambda."(\$_v) {".$this->_tag_php_close.$code['content'].$this->_tag_php_open." } }";
$contents = str_replace($code['src'], $this->_code($block_code), $contents);
}
foreach ($inlines as $name=>$code) {
$contents = str_replace($code['src'], '', $contents);
}
$matches = array();
while (preg_match_all("/\{inline\:([a-zA-Z0-9\_\-]+)\}/s", $contents, $matches)) {
foreach ($matches[0] as $k=>$ma) {
$contents = str_replace($ma, $inlines[$matches[1][$k]]['content'], $contents);
}
}
foreach ($blocks as $name=>$code) {
$contents = str_replace("{block:".$name."}", $this->_code($name."_".$lambda."(&\$_v);"), $contents);
}
return $contents;
}
/** Parse expression syntax: if, elseif, foreach, else, for, eval, eval_literal */
function _parse_expressions($contents)
{
// foreach parsing
if (preg_match_all("/\{foreach (.+)\}/sU", $contents, $matches)) {
foreach ($matches[1] as $k=>$exp) {
$exp = trim(trim($exp,"()"));
list($e_left, $e_right) = explode(" as ", $exp);
$e_right = explode("=>", $e_right);
$left_exp = $this->_split_exp($e_left);
$code = "";
if (substr($left_exp, 0, 5) !== "array") {
$code = "if(!empty(".$left_exp."))";
}
$code .= "foreach(".$left_exp." as ".$this->_split_exp($e_right[0]);
if (count($e_right)==2) {
$code .= '=>'.$this->_split_exp($e_right[1]);
}
$code .= '){';
$contents = str_replace($matches[0][$k], trim($this->_code($code)), $contents);
}
}
// if & for & elseif parsing
if (preg_match_all("/\{(if|elseif|for|while) (.+)\}/sU", $contents, $matches)) {
foreach ($matches[1] as $k=>$v) {
if ($v=="for") {
$matches[2][$k] = trim($matches[2][$k],"()");
}
$code = $v."(".$this->_split_exp($matches[2][$k])."){";
if ($v=="elseif") {
$code = "}".$code;
}
$contents = str_replace($matches[0][$k], $this->_code($code), $contents);
}
}
// eval & eval_literal parsing
if (preg_match_all("/\{(eval|eval_literal) (.+)\}/sU", $contents, $matches)) {
foreach ($matches[1] as $k=>$type) {
$code = rtrim(trim($matches[2][$k]),';');
if ($type=="eval") {
$code = $this->_split_exp($code);
}
$code .= ";";
$contents = str_replace($matches[0][$k], $this->_code($code), $contents);
}
}
$contents = str_replace("{else}", $this->_code("}else{"), $contents);
$contents = str_replace(array("{/foreach}","{/while}","{/for}","{/if}"), trim($this->_code("}")), $contents);
return $contents;
}
/** Parse variables */
function _parse_variables($contents)
{
$mycontent = preg_replace("/\<\?php.+\?\>/sU","",$contents);
// [a-zA-Z\_\$\"\'\[\]\ ]
if (preg_match_all("/\{([^\{]+)\}/sU", $mycontent, $matches)) {
foreach ($matches[1] as $k=>$v) {
if (strstr($v,"\n")===false && $v{0}!=" ") {
if ($v{0}!='$' && !in_array($v{0}, array("'",'"'))) {
// shorthand variables {v}
$v = '$'.$v;
}
$code = "";
if (strstr($v,"|")!==false) {
list($left,$right) = explode("|",$v);
$left = $this->_split_exp($left);
switch ($right) {
case "toupper": $right = "strtoupper"; break;
case "tolower": $right = "strtolower"; break;
case "escape": $code = "echo htmlspecialchars(".$left.", ENT_QUOTES);"; break;
}
if ($code=='') {
$code = "echo ".$right."(".$left.");";
}
} else {
$code = "echo ".$this->_split_exp($v).";";
}
$contents = str_replace($matches[0][$k], $this->_code($code), $contents);
}
}
}
return $contents;
}
/** Split up variables from a php expression and replace them with actual variable locations */
function _split_exp($exp)
{
$code = str_replace(".","__1","<"."?php if (".$exp.") { ?".">");
$tokens = token_get_all($code);
$objects = array();
$variable = false;
$variables = array();
$variable_continues = false;
foreach ($tokens as $k=>$v) {
if (is_array($v)) {
if ($v[0] == T_OBJECT_OPERATOR) {
$variable_continues = false;
$objects[] = $variable;
}
if ($v[0] == T_VARIABLE) {
if (!$variable_continues && isset($variable) && !in_array($variable,$objects)) {
$variables[] = $variable;
}
$variable = $variable_continues ? $variable.$v[1] : $v[1];
if (strstr($variable,"__1")!==false) {
$variable = str_replace("__1",".",$variable);
$variable_continues = false;
if (substr($variable,-1)==".") {
$variable_continues = true;
}
} else if ($variable_continues) {
$variable_continues = false;
}
}
$v[0] = token_name($v[0]);
$tokens[$k] = $v;
}
}
if (isset($variable) && !in_array($variable,$variables) && !in_array($variable,$objects)) {
$variables[] = $variable;
}
// globalize objects
foreach ($objects as $object) {
if ($object!='$this' && is_object($GLOBALS[substr($object,1)])) {
$this->_global_variables[] = $object;
} else {
$variables[] = $object;
}
}
// closure to sort vars by length and alphabetically
usort($variables, function($a, $b) {
if (strlen($a)==strlen($b)) {
if ($a==$b) {
return 0;
}
return ($a<$b) ? 1 : -1;
}
return (strlen($a)<strlen($b)) ? 1 : -1;
} );
foreach ($variables as $var) {
if ($var != '$this') {
$exp = str_replace($var, $this->_get_var($var), $exp);
}
}
return $exp;
}
/** Helper function for replacing tags into actual variable locations */
function _get_var($var) {
$left_modifier = substr($var,1); // remove $
$retval = $var;
if ($var{0}!='"' && $var{0}!="'") {
$retval = '$_v';
if (strstr($left_modifier,'.')!==false) {
// we have ourselves a table index
$table_indices = explode('.',$left_modifier);
foreach ($table_indices as $v) {
$retval .= (($v{0}=='$') ? "[".$this->_get_var($v)."]" : "['".$v."']");
}
} else {
$retval .= (($left_modifier{0}=='$') ? "[".$this->_get_var($left_modifier)."]" : "['".$left_modifier."']");
}
}
return $retval;
}
/** Helper function for php code shorthand syntax, optimizing compiler size */
function _code($s) {
return $this->_tag_php_open." ".$s.$this->_tag_php_close;
}
}

View File

@@ -0,0 +1,182 @@
<?php
namespace Monotek\MiniTPL;
/*
Tit Petrič, Monotek d.o.o., (cc) tit.petric@monotek.net
http://creativecommons.org/licenses/by-sa/3.0/
*/
/** Template class */
class Template
{
const E_TEMPLATE_COMPILE = "Template file '%s' doesn't exist! Is the compile dir writable?";
const E_FILENAME_EMPTY = "Filename can't be empty, tried to render '%s'";
/** Holds search paths */
var $_paths;
/** Compile location, relative or absolute */
var $_compile_location, $_compile_absolute;
/** Defaults */
var $_defaults;
/** Hold assigned values, filenames, stack */
protected $stack = array();
protected $filename;
protected $source;
protected $vars;
/** Default constructor */
function __construct($paths=false)
{
$this->set_compile_location("cache/", false);
$this->set_paths($paths);
$this->_defaults = array(array("ldelim","{"), array("rdelim","}"));
$this->_nocache = false;
}
function add_default($k,$v='')
{
$this->_defaults[] = array($k,$v);
}
function _default_vars()
{
if (empty($this->stack)) {
$this->vars = array();
}
$this->filename = false;
foreach ($this->_defaults as $v) {
$this->assign($v[0],$v[1]);
}
}
function push()
{
$this->stack[] = $this->filename;
}
function pop()
{
list($this->filename) = array_splice($this->stack, -1);
}
/** Template loader */
function load($filename)
{
$r = 0;
$this->_default_vars();
if (($path = $this->_find_path($filename))!==false) {
$f_original = $path.$filename;
$f_compiled = $this->_compile_path($path).$filename;
if (file_exists($f_compiled)) {
$r = 1;
if (file_exists($f_original) && (filemtime($f_original) > filemtime($f_compiled))) {
$r = $this->compile($f_original, $f_compiled);
}
} else {
$r = $this->compile($f_original, $f_compiled);
}
$this->filename = $f_compiled;
if (!$r) {
throw new \Exception(sprintf(self::E_TEMPLATE_COMPILE, $filename));
}
}
$this->source = $filename;
return (bool)$r;
}
/** Compile template */
function compile($s,$d)
{
$c = new Compiler;
return $c->compile($s,$d,array(&$this,"_find_path"),$this->_nocache);
}
/** Sets searchable template paths */
function set_paths($paths=false) {
if ($paths===false) {
$paths = array("templates/");
}
if (is_string($paths)) {
$paths = func_get_args();
}
$this->_paths = $paths;
}
/** Sets compile location */
function set_compile_location($path, $is_absolute)
{
$this->_compile_location = rtrim($path,"/")."/";
$this->_compile_absolute = $is_absolute;
}
/** Compile path calculation */
function _compile_path($path)
{
if ($this->_compile_absolute) {
return $this->_compile_location.$path;
}
return $path.$this->_compile_location;
}
/** Finds first path with existing template file */
function _find_path($filename) {
foreach ($this->_paths as $path) {
// even if only compiled template exists, it's ok
if (file_exists($path.$filename) || file_exists($this->_compile_path($path).$filename)) {
return $path;
}
}
return false;
}
/** Assign data to the template */
function assign($key,$value='')
{
if (is_array($key)) {
// $key is an array, use $value as prefix if set
if ($value != '') {
$value .= '_';
}
foreach ($key as $k=>$v) {
$this->vars[$value.$k] = $v;
}
} else {
// $key is a string, do stuff depending on value and prefix
$concat = ($key{0}=='.');
if ($concat) {
$key = substr($key,1);
}
$this->vars[$key] = ($concat ? (is_array($value) ? array_merge($this->vars[$key],$value) : $this->vars[$key].$value) : $value);
}
return ""; // {$this->assign} calls, ouch
}
/** Get variable */
function getVar($key)
{
return isset($this->vars[$key]) ? $this->vars[$key] : false;
}
/** Render the template to standard output */
function render()
{
if ($this->filename === false) {
throw new \Exception(sprintf(self::E_FILENAME_EMPTY, $this->source));
}
include($this->filename);
}
/** Render the template and return text */
function get()
{
ob_start();
$this->render();
$s = ob_get_contents();
ob_end_clean();
return $s;
}
}

View File

@@ -0,0 +1,34 @@
{
"name": "monotek/minitpl",
"description": "Miniature fully featured PHP template engine",
"keywords": ["monotek", "minitpl", "template", "php", "tiny", "small", "smarty"],
"homepage": "https://github.com/titpetric/minitpl",
"authors": [
{
"name": "Tit Petric",
"email": "tit.petric@monotek.net",
"homepage": "http://titpetric.github.io",
"role": "developer"
}
],
"license": "CC BY-SA 3.0",
"type": "library",
"require": {
"php": ">=5.3.0"
},
"autoload": {
"psr-0": {
"Monotek\\MiniTPL": "code"
}
}
}

View File

@@ -0,0 +1,783 @@
Monotek Mini Template
=====================
Last update - Tue 18 Dec 2012 11:24:08 AM CET
by Tit Petrič ( tit.petric@monotek.net ) / Monotek d.o.o.
Introduction
------------
This is a production stable version of our internal templating
system, which supports template compilation, and is PHP4 and
PHP5 compatible. It uses some very advanced PHP functionality,
to keep down the size of the code and at the same time still
provide exceptional functionality.
The total code is 13.5KB and is propperly indented, contains
minimal commenting and perfectly readable with all white space
left in tact.
The code falls under the [Creative Commons Attribution -
Share Alike](http://creativecommons.org/licenses/by-sa/3.0/) license.
Installation and Requirement
----------------------------
Monotek Mini Template requires PHP version 4.3.0 or later.
It can work with lower php versions also, if you provide
your own [file_get_contents()](http://php.net/file_get_contents) function.
Depending on the folder location where you use the template object,
it will need the following folders:
> templates/<br/>
> templates/cache/ <span class="red">*</span>
Since this templating system is a compiling template system,
you will need a writable cache directory.
Language reference
------------------
Since syntax for the template system is loosely based on Smarty
and PHP language syntax, knowing some PHP basics while using this
template system will help you a long way.
1. <a href="#variables">Language constructs</a>
2. <a href="#loops">Loops</a>
3. <a href="#conditions">Conditions</a>
4. <a href="#blocks">Block and Inline definitions</a>
5. <a href="#advanced">Advanced, embedding php code</a>
6. <a href="#php">PHP usage reference</a>
----------------------------------------------------------
<h3 id="variables">1. Language constructs <a href="#top">Δ Jump to top</a></h3>
Theese basic language constructs provide you with some insight
into the workings of the template system, so you can start to
create your own templates.
Let me just start by saying, all language constructs are
defined between curly brace tokens (`{` and `}`). In case
the template engine doesn't recognise the token, it leaves
it as-is, with curly braces in tact. This should make the
template engine javascript/json safe, no escaping is required.
#### 1.1. Variables
Using variables from templates is easy. Variables are enclosed
in curly braces like so: `{variable}`. Since variables are parsed
on the last stage, prefixing variables with the dollar sign is
optional. So, `{$variable}` is the same as `{variable}`.
~~~~~~~~~~~~~
{variable} is the same as {$variable}
~~~~~~~~~~~~~
#### 1.2. Arrays
There is a shorthand syntax for using arrays inside a template.
The array index separator is a dot (`.`). Consecutive dots can be
used for traversing into array depth like `{$array.items.0}`, or
even variables starting with `$`, like `{$sections.$news_section.title}`.
PHP syntax for arrays is also supported.
~~~~~~~~~~~~
{$array.items} is the same as {$array['items']}
{$array.items.0} is the same as {$array['items'][0]}
{$array.$items.0} is the same as {$array[$items][0]}
~~~~~~~~~~~~
#### 1.3. Modifiers
Modifiers are PHP functions, which take one arbitrary value
(usually, string), and return a string, which gets shown.
Some functions in PHP can be used as modifiers
(strtoupper, ucfirst, str_rot13, strrev, count, ...).
~~~~~~~~~~~~~~~~~~~~~~~~
/** Custom modifier example */
function add_it_up($array)
{
$size = 0;
foreach ($array as $value) {
$size += $value['size'];
}
return $size;
}
~~~~~~~~~~~~~~~~~~~~~~~~
To use the above function in a template, just do `{$variable|add_it_up}`,
which will loop trough all `$variable` items and add up the value of
the size element and return the total sum of all sizes.
You can't pass additional parameters to modifiers. If you need to
do that, then take a look at <a href="#advanced">advanced templating</a>,
which will show you a way to embed php code inside a template.
The modifier `escape` is a special template modifier, which
gets replaced by a `htmlspecialchars($left, ENT_QUOTES);` call.
It is used for escaping data, that might contain quotes or `<`, `>`.
Here are a few examples of the correct use of the escape modifier.
~~~~~~~~~~~~~~~
<input type="title" type="text" value="{title|escape}"/>
<textarea name="content">{content|escape}</textarea>
<a href="{news.link}" title="{news.title|escape}">Read more ...</a>
<h3>{site.title|escape}</h3>
~~~~~~~~~~~~~~~
Aditional template modifiers are `toupper` for `strtoupper` and
`tolower` for `strtolower`. No additional functions are created
for theese special modifiers.
~~~~~~~~~~~~~~
{variable|escape}
{variable|toupper}
{variable|add_id_up}
~~~~~~~~~~~~~~
The above code gets compiled to:
~~~~~~~~~~~~~~
<?php
$_v = &$this->vars;
echo htmlspecialchars($_v['variable'], ENT_QUOTES);
echo strtoupper($_v['variable']);
echo add_id_up($_v['variable']);
?>
~~~~~~~~~~~~~~
A common use for modifiers is outputting data for javascript,
using the php function `json_encode`.
#### 1.4. Objects
You can also call object methods as modifiers, from global or
local objects. The compiler will determine at run-time, if
you have a global object by the name, and modify the code
accordingly.
~~~~~~~~~~~
{$variable|$memcache->get}
~~~~~~~~~~~
If the global object doesn't exist, it assumes a local object.
~~~~~~~~~~~
<?php
$_v = &$this->vars;
echo $_v['memcache']->get($_v['variable']);
?>
~~~~~~~~~~~
However, if a global object by the name $memcache exists at
compile time:
~~~~~~~~~~
<?php
$_v = &$this->vars;
global $memcache;
echo $memcache->get($_v['variable']);
?>
~~~~~~~~~~
You can also use variables from objects, in the same way.
~~~~~~~~~~~~
{$memcache->variable}
~~~~~~~~~~~~
Compiles to one of theese:
~~~~~~~~~~
<?php
$_v = &$this->vars;
echo $_v['memcache']->variable;
?>
~~~~~~~~~~
~~~~~~~~~~
<?php
$_v = &$this->vars;
global $memcache;
echo $memcache->variable;
?>
~~~~~~~~~~
#### 1.5. Constants
Value starting with `_` is assumed to be a constant. The template
system will output the value of the constant if defined,
or just the name of the constant. Item `{_MY_CONSTANT}` will be
used as a constant, because of those rules, however `{$_my_variable}`
wouldnt be, since it starts with the variable identifier `$`.
~~~~~~~~~~~
{_MY_CONSTANT}
{_this_is_also_a_constant}
{$_my_variable}
~~~~~~~~~~~
The above code gets compiled to:
~~~~~~~~~~~
<?php
$_v = &$this->vars;
echo _MY_CONSTANT;
echo _this_is_also_a_constant;
echo $_v['_my_variable'];
?>
~~~~~~~~~~~
#### 1.6. Includes
The statement `{include filename.tpl}` gets replaced with the content
of the specified template, before it does any processing. In case
you change the filename.tpl template, you have to regenerate the
cache of the main template manually (by deleting the cache it or
updating the source template, so the timestamp is modified).
There is no limit to the number of files you can include.
~~~~~~~~~~~
<html>
<head>
<title>{$title}</title>
</head>
<body>
{include site_header.tpl}
{$contents}
{include site_footer.tpl}
</body>
</html>
~~~~~~~~~~~
If you need to include template files dynamically, you can use `{load}` to
do this. This will solve some caching problems and enable you to use templates
based on what you pass to the main template as variables via `assign`.
Keep in mind that with this you can't use the `inline` and `block` definitions
outside the loaded template, but with `include` you can.
~~~~~~~~~~~
<html>
<head>
<title>{$title}</title>
</head>
<body>
{load $dynamic_header}
{load $dynamic_contents_template}
{load $dynamic_footer}
</body>
</html>
~~~~~~~~~~~
#### 1.7. Comments
Comments are enclosed between `{*` and `*}`. Comments are stripped
at compile time, and don't show up in the compiled code or in
the template engine output. This is useful for extensive documentation
if needed, since it doesn't give any overhead at run-time.
~~~~~~~~~~~
{* This is a comment that won't be shown anywhere,
except in the source template, only to developers. *}
Hello world!
~~~~~~~~~~~
The above compiles to:
`Hello world!`
<h3 id="loops">2. Loops <a href="#top">Δ Jump to top</a></h3>
#### for, foreach, foreach / else, while
Since the main goal when doing pseudo code here is to make it
friendly for the developer, syntax is PHP inspired. While
this isn't ideal for web designers that make templates, it
is ideal for the majority of the PHP developers, since they
don't need to learn a new language for templating.
~~~~~~~~~~~~
<html><body>
{for $i=0; $i<count($array); $i++}
Hop number {i}<br/>
{/for}
{foreach $array as $value}
I like values like I like: {value}<br/>
{/foreach}
{foreach $array as $key=>$value}
I like my value {value} to have a key {key}.<br/>
{/foreach}
{foreach $array as $key=>$value}
I like my value {value} to have a key {key}.<br/>
{else}
I'm sorry, I have nothing in the array.<br/>
{/foreach}
{while ($k++ < 10)}
Well, k is {k}<br/>
{/while}
</body></html>
~~~~~~~~~~~~
You see, this is very friendly for a PHP developer. Lets compare
it to the original PHP code (two ways):
~~~~~~~~~~~~
echo '<html><body>';
for ($i=0; $i<count($array); $i++) {
echo 'Hop number '.$i.'<br/>';
}
foreach ($array as $value) {
echo 'I like values like I like: '.$value.'<br/>';
}
foreach ($array as $key=>$value) {
echo 'I like my value '.$value.' to have a key '.$key.'.<br/>';
}
if (!empty($array)) {
foreach ($array as $key=>$value) {
echo 'I like my value '.$value.' to have a key '.$key.'.<br/>';
}
} else {
echo "I'm sorry, I have nothing in the array.<br/>";
}
while ($k++ < 10) {
echo "Well, k is ".$k."<br/>";
}
echo '</body></html>';
~~~~~~~~~~~~
While this example illustrates the similarity in syntax,
it also shows how writing templates is actually less time
consuming and more readable in the long run.
If you notice, the statements all support full PHP syntax.
While not very obvious with `foreach`, you can see that
the code is practically identical for other statements,
where you can mix the complete PHP syntax along with
template features like the table addressing shorthand.
~~~~~~~~~~~~~~~~~~~
<p>{foreach $items.news as $newsitem}<b>-</b>{/foreach}</p>
<p>{foreach $items['news'] as $newsitem}<b>+</b>{/foreach}</p>
<p>{for $i=0; $i<count($items.news); $i++}<b>!</b>{/for}</p>
<p>{for $i=0; $i<count($items.news)+count($items['news']); $i+=2}<b>?</b>{/for}</p>
~~~~~~~~~~~~~~~~~~~
<h3 id="conditions">3. Conditions <a href="#top">Δ Jump to top</a></h3>
The syntax for the `if` and `elseif` statements are
PHP compatible. You can call functions, methods, use
constants and arithmetic operations. Variables are parsed
with the templating extensions for using arrays, so both
syntaxes can be used at the same time.
#### 3.1. if statement
~~~~~~~~~~~~~
{if $is.admin && $user['name']=="black"}
{* Hello black! Only you can edit things,
but only as long as you stay an admin. *}
...
{/if}
~~~~~~~~~~~~~
#### 3.2. if/elseif/else statement
~~~~~~~~~~~~~
{if $is.admin && $user['name']=="black"}
{* Hello black! Only you can edit things,
but only as long as you stay an admin. *}
...
{elseif $is_moderator}
{* Hello moderator! You can do some things. *}
{else}
{* You are a nobody and you earn nothing! *}
{/if}
~~~~~~~~~~~~~
#### 3.3. foreach/else statement
~~~~~~~~~~~~~
{foreach $newsitems.$section.items as $item}
<div class="newsitem">
<h3 class="title">{item.title}</h3>
<div class="content">{item.content}</div>
</div>
{else}
<div class="notice">
No newsitems exist in the chosen section.
</div>
{/if}
~~~~~~~~~~~~~
#### 3.4. nocache
Minitpl template files are translated into PHP code to give you
the best possible execution speed. Using `include` statements
sometimes makes it hard to invalidate this cache, so for
development purposes we have included a `nocache` directive.
This erases your template cache after the file has been used once.
~~~~~~~~~~~~
{*nocache*}
~~~~~~~~~~~~
This behaviour needs to be activated by setting `$tpl->_nocache` to `true`.
<h3 id="blocks">4. Block and Inline definitions <a href="#top">Δ Jump to top</a></h3>
Depending on the usage, you might want to reuse pieces of
the template multiple times in the same or multiple templates.
You can achieve this by using Block and Inline definitions.
The difference between a block and inline definition is,
that the `block` definition defines a PHP function, and
can be used recursively in the template, for example, to
traverse a tree structure. The `inline` definition only
allows the same piece of template code to be reused multiple
times.
#### 4.1 block definition and usage
~~~~~~~~~~~
{block recurse}
{if $i++ < 10}
call {i}
{block:recurse}
{/if}
{/block}
{block:recurse}
~~~~~~~~~~~
The compiled template will look something like this:
~~~~~~~~~~~
<?php
/* This code has been cleaned up some, for
documentation purposes. It illustrates
the compile aspect of blocks, but is not
a carbon copy of the compiled template. */
$_v = &$this->vars;
function recurse_1213891842_673($_v) {
if ($_v['i']++ < 10) {
echo "call ".$_v['i'];
recurse_1213891842_673(&$_v);
}
}
recurse_1213891842_673(&$_v);
?>
~~~~~~~~~~~
As you can see, the block definition gets compiled into
a function definition, which lives in the same variable space.
While traversing a tree this way isn't very practical,
it is however possible. You probablly won't ever need
this functionality. In our experience recursion itself
is very rare, and even if used, it is handled on the PHP
level, and not the templating level.
You can use `block` definitions instead of `inline` definitions
if you are worried about code overhead. I would consider this
when an `inline` definition is multiple kilobytes in size and
is beeing used extensively in the same template.
#### 4.2 inline definition and usage
The inline keyword comes from `C++`, where the compiler
would replace the calls to an inline function definition
with the function itself. This is a speed gain for
`C++`, since calling a function many times is more
expensive, than copying the code around. The practical
reason for this inside a template goes along the same
train of thought.
~~~~~~~~~~
{inline newsitem}
<div class="news">
<h3 class="title">{news.title}</h3>
<div class="content">{news.content}</div>
</div>
{/inline}
<div class="top_news">
{foreach $newsitems.top.items as $news}{inline:newsitem}{/foreach}
</div>
<div class="other_news">
{foreach $newsitems.$section.items as $news}{inline:newsitem}{/foreach}
</div>
~~~~~~~~~~
Usage of `inline` definition decreases template size before
compilation, and helps with design standardisation. Same page
components can be literally re-used inside the template and
changing one aspect of the design is done in one place instead
of every place the same code snippet is used.
<h3 id="advanced">5. Advanced, embedding php code <a href="#top">Δ Jump to top</a></h3>
Since the above language constructs only take care of the
read only aspect of template programming, there is sometimes
also a need to modify variables inside templates for various
uses. The `eval` and `eval_literal` constructs take care
of that. The `php` construct is ment for more advanced
operations, and like `eval` lives in the template variable
space.
#### 5.1 eval
The eval construct allows quick operations on local
template variables. For example, if you want to build
a table with alternating css styles on rows, you would
do something like this:
~~~~~~~~~~~~~
{eval $style="even";}
<table>
{foreach $rows as $row}
{eval $style = ($style=="even") ? "odd" : "even"}
<tr class="{style}">
<td>{row.message}</td>
</tr>
{/foreach}
</table>
~~~~~~~~~~~~~
All variables when using the `eval` construct, are
mapped to local template variables.
It is not ok to use variables named or starting with
`$_v`, since that will result in naming errors on
compile time, and you won't be able to use your data
in the template, since it's in the wrong location.
#### 5.2 eval_literal
When you need global variables and objects, to execute more complex
code, you can use the `eval_literal` construct. The code inside
does not get evaluated, meaning it is kept as-is.
~~~~~~~~~~~
{eval_literal
global $cms_module;
$_v['menu_data'] =
$cms_module->get_menu("branch", array("item","menu")); }
~~~~~~~~~~~
#### 5.3 php
~~~~~~~~~~~~~~~
{php}
function mygettime()
{
return array("time"=>time(),"date"=>date("r"),"microtime"=>microtime());
}
$mygettime = mygettime();
{/php}
{mygettime|var_dump}
~~~~~~~~~~~~~~~
The above code gets compiled into:
~~~~~~~~~
<?php
$_v = &$this->vars;
function mygettime()
{
return array("time"=>time(),"date"=>date("r"),"microtime"=>microtime());
}
$_v['mygettime'] = mygettime();
echo var_dump($_v['mygettime']);
?>
~~~~~~~~~
This is by far the least used construct and also least tested.
If you want to build objects or functions or use a lot of PHP
code inside the template, you are definetly doing something wrong,
even if supported by the templating system.
It is not ok to use variables named or starting with
`$_v`, since that will result in naming errors on
compile time, and you won't be able to use your data
in the template, since it's in the wrong location.
<h3 id="php">6. PHP usage reference <a href="#top">Δ Jump to top</a></h3>
This section is about functionality of the template system in PHP.
You already know how to make templates like the ones above, this
teaches you how to create the PHP code, that uses the templates.
#### 6.1 Basic usage
For basic usage, you need to include the file `class.template.php`.
The functions used include `class.template_compiler.php` if needed.
The following methods are exported:
`load`, `assign`, `render`, `get`, `set_paths`, `set_compile_location`, `compile`
You will probablly only use the first four methods unless you
want to change the default paths, where the system is searching for
templates, or if you want to do your own compiling of templates
for whatever reason.
##### 6.1. load ( string $filename )
With this method you specify which template file to load. It will
search trough the configured paths (Default: `templates/`) and
use the template. Calling this method resets the template to it's
defaults, and you have to fill up the content using the `assign`
method.
##### 6.2. assign ( mixed $key, [ mixed $value = '' ] )
This is the most complex method available, and it's behaviour is
dependant on the parameters supplied.
If the first parameter is an array, and the second parameter stays
at the default value, an entry will be created for each key and value
pair in the first parameter.
If the first parameter is an array, and the second parameter is
a string value, an entry will be created for each key and value
pair in the first parameter, using the second parameter as a prefix
for each key.
If the first parameter is a string, the second parameter will be
assigned as it's value. The second parameter can be any PHP type.
~~~~~~~~
$data = array(); // some example data
$data['title'] = "Leno promises smooth transition to O'Brien";
$data['content'] = "For months, Fallon has been widely considered
the top choice to succeed OBrien when he steps
down next year. On Thursday, published reports ...";
/* This will define an entry {timestamp} */
$tpl->assign("timestamp", time());
/* This will define entries {title} and {content} */
$tpl->assign($data);
/* This example will define {news_title} and {news_content} */
$tpl->assign($data, "news");
/* This will define the item {news}, which contains an array.
You can output the fields with {news.title} and {news.content} */
$tpl->assign("news", $data);
~~~~~~~~
##### 6.3. render and get
Theese methods dont have any arguments. Template compilation is
done in the background, if needed. The method `render` outputs
the data to standard output, while the method `get` simply returns
it, in order to use it in PHP.
##### 6.4. set_paths ( [ $paths = false ] )
The method `set_paths`, takes an array, with possible locations
for template files. The compiled location is set with the
`set_compile_location` function documented later. Paths passed to
this function, must end with a trailing slash.
The template system will traverse the `$paths` array searching
for template files, until one is found. If none are found, an
error is printed, and the script execution is terminated.
This is useful if you have multiple template locations, which
are overridable. For example, you can have the following CMS
structure.
~~~~~~~~~~~~~~~
$paths = array();
/* This is the most important location, everything
can be overriden from inside the theme. */
$paths[] = "theme/templates/";
/* This is the second most important location,
it usually defines the look of the cms modules */
$paths[] = "modules/".$module_name."/templates/";
/* This is the least important template location,
it usually provides system wide templates, like
a paginator template, an xml / rss template, or
other very general templates. */
$paths[] = "include/templates/";
$tpl->set_paths($paths);
~~~~~~~~~~~~~~~
Templating constructs like `{include}` are affected by the path
settings. The included templates use the same paths in the same
order, until a needed template is found.
The following syntax is also allowed since 2012/05/24:
~~~~~~~~~~~~~~~
$tpl->set_paths("theme/templates/","modules/".$module_name."/templates/","include/templates/");
~~~~~~~~~~~~~~~
##### 6.5. $this->set_compile_location( [ $compile_location = "cache/" [ , $is_absolute = false ] ] );
By default the compile location of the templates is relative to the
source template folder. So, with the template locations listed above,
you would need a `cache/` folder under every location, to store the compiled
template files for later use.
You can modify this behavior with the `set_compile_location` function.
You can set the cache location to a common folder, and mark it as
an absolute location (as opposed to the default, relative path).
~~~~~~~~~~~~~~~
$tpl->set_compile_location("/tmp/minitpl", true);
~~~~~~~~~~~~~~~
The example will store all your templates under `/tmp/minitpl`. This
location needs to be writable. The compiled files under the location
can be safely deleted any time, if you want to regenerate them.
When caching, a full path structure will be created, to avoid any
conflicts between templates, that use same names. For example:
If you have a template named `site.tpl` under two distinct locations like
`theme/templates/` and `theme/issue2012/templates/` and use both sources,
the conflict is resolved by creating the following structure in the
cache location:
~~~~~~~~~~~~~~~
/tmp/minitpl/theme/templates/site.tpl
/tmp/minitpl/theme/issue2012/templates/site.tpl
~~~~~~~~~~~~~~~
This can let you change your source folders with `set_paths~ and and the
same time only have one cache location for all your templates. When
you want to expunge any stale cache files or use minitpl in a more complex
system, this is a much simpler approach for cache management.

View File

@@ -0,0 +1,705 @@
Monotek Mini Template
=====================
Zadnje posodobljeno - Tue 18 Dec 2012 11:24:30 AM CET
Avtor: Tit Petrič ( tit.petric@monotek.net ) / Monotek d.o.o.<br/>
Predstavitev
------------
Stabilna Produkcijska različica podpira sestavljanje šablone in je združljiva
s PHP4 in PHP5. Za ohranjanje majhne velikosti kode uporablja zelo napredne
funkcije, ob tem pa poskrbi za izjemno uporabnost.
Celotna koda je velika 13,5 KB in vsebuje minimalno količino komentarjev in
zamik kode z uporabo tabulatorjev za berljivost.
Koda je zaščitena z [Creative Commons Priznanje Avtorstva - Deljenje pod
enakimi pogoji](http://creativecommons.org/licenses/by-sa/3.0/) licenco.
Namestitev in potrebna programska oprema
----------------------------
Za delovanje Monotek Mini Template potrebujete PHP različice 4.3.0 ali novejšo.
Sistem lahko deluje tudi s starejšimi PHP različicami, če priskrbite svojo
[file_get_contents()}(http://php.net/file_get_contents) funkcijo.
Glede na mapo, kjer uporabljate template objekt, potrebujete mape:
> templates/<br/>
> templates/cache/ <span class="red">*</span>
Ker pa se ta template sistem sestavlja, boste potrebovali tudi cache mapo.
Jezikovna vezava
------------------
Ker je način uporabe template sistema podoben Smarty template sistemu in PHP
programskem jeziku, vam bo poznavanje PHP osnov v pomoč pri uporabi tega
template sistema.
1. <a href="#variables">Sestava jezika</a>
2. <a href="#loops">Zanke</a>
3. <a href="#conditions">Pogoji</a>
4. <a href="#blocks">Block in Inline definicije</a>
5. <a href="#advanced">Napredno, vstavljanje PHP kode</a>
6. <a href="#php">Uporaba template sistema v PHP</a>
----------------------------------------------------------
<h3 id="variables">1. Jezikovna sestava <a href="#top">Δ Skok na vrh</a></h3>
Jezikovna sestava vam ponuja razlago delovanja sistema, da boste lahko pričeli
z ustvarjanjem in uporabo svojih template datotek. Prva stvar, katero je
potrebno povedat je, da so vse jezikovne sestave definirane med zavitima
oklepajema (`{` in `}`). Če template sistem ne prepozna jezikovne sestave, je
ne prevaja. To omogoča uporabo javascript in json zapisov brez težav.
#### 1.1. Spremenljivke
Uporaba spremenljivk je enostavna. Spremenljivke so zaprte med zavite oklepaje:
`{spremenljivka}`. Ker so spremenljivke prevedene v zadnji stopnji, je uporaba
znaka dolar pred njimi neobvezna. Zato ni nobene razlike med zapisom
`{$spremenljivka}` in zapisom `{spremenljivka}`, oba sta pravilna.
~~~~~~~~~~~~~
{spremenljivka} je enako kot {$spremenljivka}
~~~~~~~~~~~~~
#### 1.2. Množice
Obstaja kratek način uporabe množic znotraj template. Če želite izpisati
spremenljivko, ki se nahaja znotraj množice, za naslavljanje uporabite operator
`.` (pika). Množice lahko naslavljate v poljubno globino kot `$mnozica.tocka.0`
ali pa tudi z uporabo spremenljivk, ki se začnejo z znakom dolar
`{$mnozica.$novice.naslov}`. Prav tako je mogoča uporaba PHP jezikovne sestave
kot je razvidno iz spodnjega primera.
~~~~~~~~~~~~
{$array.items} je enako kot {$array['items']}
{$array.items.0} je enako kot {$array['items'][0]}
{$array.$items.0} je enako kot {$array[$items][0]}
~~~~~~~~~~~~
#### 1.3. Prikrojevalci
Prikrojevalci so PHP funkcije, ki iz poljubne vrednosti (ponavadi besede)
vrnejo besedo, ki se prikaže. Nekatere PHP funkcije se lahko uporabljajo
kot prikrojevalci (strtoupper, ucfirst, str_rot13, strrev, count, ...).
~~~~~~~~~~~~~~~~~~~~~~~~
/** Primer uporabe prikrojevalcev */
function add_it_up($array)
{
$size = 0;
foreach ($array as $value) {
$size += $value['size'];
}
return $size;
}
~~~~~~~~~~~~~~~~~~~~~~~~
Za uporabo zgornje funkcije kot prikrojevalca v template datoteko vpišite
`{$variable|add_it_up}`. Prikrojevalec bo šel čez vse elemente spremenljivke
in seštel vrednosti pod ključem `size`, ter izpisal seštevek.
Podajanje dodatnih parametrov prikrojevalcem ni omogočeno. Če ne morete okoli
tega, potem si oglejte kategorijo <a href="#advanced">naprednih zmožnosti</a>
kjer boste zvedeli za način, kako vključiti PHP kodo v template datoteko.
Template sistem vključuje poseben prikrojevalec z imenom `escape`, kater se
zamenja z klicem `htmlspecialchars($left, ENT_QUOTES);`. Ta prikrojevalec vam
služi za izpis podatkov znotraj HTML, kateri mogoče vsebujejo narekovaje ali
pa znake `<` (manjše) in `>` (večje). V primeru je demonstrirana pravilna
uporaba uporaba tega prikrojevalca.
~~~~~~~~~~~~~~~
<input type="title" type="text" value="{title|escape}"/>
<textarea name="content">{content|escape}</textarea>
<a href="{news.link}" title="{news.title|escape}">Read more ...</a>
<h3>{site.title|escape}</h3>
{content} {* pričakujemo HTML, tukaj ne rabimo escape *}
~~~~~~~~~~~~~~~
Dodatna template prikrojevalca sta še `toupper` za `strtoupper` in `tolower`
za `strtolower`. Za naštete prikrojevalce ni narejenih posebnih funkcij.
~~~~~~~~~~~~~~
{variable|escape}
{variable|toupper}
{variable|add_id_up}
~~~~~~~~~~~~~~
Zgornja koda se prevede v naslednje:
~~~~~~~~~~~~~~
<?php
$_v = &$this->vars;
echo htmlspecialchars($_v['variable'], ENT_QUOTES);
echo strtoupper($_v['variable']);
echo add_id_up($_v['variable']);
?>
~~~~~~~~~~~~~~
V praksi se prikrojevalci pogosto uporabljajo za izpis podatkov v json notaciji
za uporabo znotraj javascript knjižnic (uporaba funkcije `json_encode`)
#### 1.4. Objekti
Objekte lahko kličete tudi kot prikrojevalce, za globalne ali lokalne objekte.
Ob prevajanju se določi, če obstaja globalni objekt z navedenim imenom in
se glede na to prilagodi prevedeno kodo.
~~~~~~~~~~~
{$variable|$memcache->get}
~~~~~~~~~~~
Če globalni objekt ne obstaja se predvideva, da obstaja lokalni objekt.
~~~~~~~~~~~
<?php
$_v = &$this->vars;
echo $_v['memcache']->get($_v['variable']);
?>
~~~~~~~~~~~
Če globalni objekt v času prevajanja obstaja se template prevede tako:
~~~~~~~~~~
<?php
$_v = &$this->vars;
global $memcache;
echo $memcache->get($_v['variable']);
?>
~~~~~~~~~~
Na enak način lahko uporabljate tudi spremenljivke iz objektov.
~~~~~~~~~~~~
{$memcache->variable}
~~~~~~~~~~~~
Kar se prevede se v naslednje:
~~~~~~~~~~
<?php
$_v = &$this->vars;
echo $_v['memcache']->variable;
?>
~~~~~~~~~~
ali
~~~~~~~~~~
<?php
$_v = &$this->vars;
global $memcache;
echo $memcache->variable;
?>
~~~~~~~~~~
#### 1.5. Konstante
Vrednost, ki ima na začetku znak `_` se smatra za konstanto. Template sistem
bo izpisal vrednost konstante, če je ta določena, ali pa ime konstante če ni.
Zaradi teh pravil bo niz `{_MOJA_KONSTANTA}` uporabljen kot konstanta, medtem
ko bo `{$_MOJA_NESPREMENLJIVKA}` uporabljen kot spremenljivka, ker se začne z
znakom za dolar `$`.
~~~~~~~~~~~
{_MY_CONSTANT}
{_this_is_also_a_constant}
{$_my_variable}
~~~~~~~~~~~
Zgornja koda se prevede v naslednje:
~~~~~~~~~~~
<?php
$_v = &$this->vars;
echo _MY_CONSTANT;
echo _this_is_also_a_constant;
echo $_v['_my_variable'];
?>
~~~~~~~~~~~
#### 1.6. Vključitev ločenih template datotek
Niz `{include imedatoteke.tpl}` se zamenja z vsebino določene template
datoteke ob začetku prevajanja. V primeru, da želite spremeniti vsebino
vključene template datoteke, morate obnoviti prevedeno kodo glavne template
datoteke. To naredite ročno (z izbrisom vsebine cache direktorija ali obnovo
časa posodobitve glavne template datoteke). V template datotekah lahko
vključite poljubno število drugih template datotek.
~~~~~~~~~~~
<html>
<head>
<title>{$title}</title>
</head>
<body>
{include site_header.tpl}
{$contents}
{include site_footer.tpl}
</body>
</html>
~~~~~~~~~~~
Če želite imeti dejansko nalaganje skupnih datotek, kar reši nekatere težave
okoli dinamičnega nalaganja in hranjenja cache datotek, potem uporabite `{load}`:
Imejte v mislih, da s to metodo ni možna uporaba `inline` in `blok` definicij
izven naložene template datoteke. To lahko dosežete z uporabo `include`.
~~~~~~~~~~~
<html>
<head>
<title>{$title}</title>
</head>
<body>
{load $dynamic_header}
{load $dynamic_contents_template}
{load $dynamic_footer}
</body>
</html>
~~~~~~~~~~~
#### 1.7. Komentarji
Komentarji so zaprti med `{*` in `*}`. Komentarji se odstranijo med prevajalnim časom in se ne prikažejo v prevedeni kodi ali izpisu. Uporabni so za izčrpno dokumentacijo, s čimer ne povzročajo povečanega časa obdelave.
~~~~~~~~~~~
{* This is a comment that won't be shown anywhere,
except in the source template, only to developers. *}
Hello world!
~~~~~~~~~~~
Kar se prevede v:
~~~~~~~~~~~
`Hello world!`
~~~~~~~~~~~
<h3 id="loops">2. Zanke <a href="#top">Δ Skok na vrh</a></h3>
#### for, foreach, foreach / else, while
Cilj sintakse je pomagati razvijalcu, zato je podobna PHP sintaksi.
Medtem ko to ni idealno za spletne programerje, ki izdelujejo template
datoteke, je idealno za veliko večino PHP razvijalcev, katerim se ni
potrebno naučiti novega programskega jezika za template sistem.
Pravtako, osnove PHP jezika kot so predstavljene, nebi smele predstavljati
večjih problemov za oblikovalce, ki že zdaj izdelujejo template datoteke
npr. za Smarty. Običajno programer naredi prvo template datoteko, oblikovalci
jo pa za tem popravljajo.
~~~~~~~~~~~~
<html><body>
{for $i=0; $i<count($array); $i++}
Hop number {i}<br/>
{/for}
{foreach $array as $value}
I like values like I like: {value}<br/>
{/foreach}
{foreach $array as $key=>$value}
I like my value {value} to have a key {key}.<br/>
{/foreach}
{foreach $array as $key=>$value}
I like my value {value} to have a key {key}.<br/>
{else}
I'm sorry, I have nothing in the array.<br/>
{/foreach}
{while ($k++ < 10)}
Well, k is {k}<br/>
{/while}
</body></html>
~~~~~~~~~~~~
Kot vidite, je template sistem zelo prijazen PHP razvijalcem.
Primerjava z originalno PHP kodo (dva načina):
~~~~~~~~~~~~
echo '<html><body>';
for ($i=0; $i<count($array); $i++) {
echo 'Hop number '.$i.'<br/>';
}
foreach ($array as $value) {
echo 'I like values like I like: '.$value.'<br/>';
}
foreach ($array as $key=>$value) {
echo 'I like my value '.$value.' to have a key '.$key.'.<br/>';
}
if (!empty($array)) {
foreach ($array as $key=>$value) {
echo 'I like my value '.$value.' to have a key '.$key.'.<br/>';
}
} else {
echo "I'm sorry, I have nothing in the array.<br/>";
}
while ($k++ < 10) {
echo "Well, k is ".$k."<br/>";
}
echo '</body></html>';
~~~~~~~~~~~~
Medtem ko ta primer prikazuje podobnost v skladnji, prikazuje tudi, kako vam
pisanje template datotek lahko vzame manj časa in je končni izdelek bolj
berljiv. Če ste morda opazili, izjave podpirajo celotno PHP sintakso.
Medtem ko to ni očitno ko pride do `foreach` zanke, lahko vidite, da je
pri ostalih primerih prevedena koda skoraj identična template kodi.
~~~~~~~~~~~~~~~~~~~
<p>{foreach $items.news as $newsitem}<b>-</b>{/foreach}</p>
<p>{foreach $items['news'] as $newsitem}<b>+</b>{/foreach}</p>
<p>{for $i=0; $i<count($items.news); $i++}<b>!</b>{/for}</p>
<p>{for $i=0; $i<count($items.news)+count($items['news']); $i+=2}<b>?</b>{/for}</p>
~~~~~~~~~~~~~~~~~~~
<h3 id="conditions">3. Pogoji <a href="#top">Δ Skok na vrh</a></h3>
Sintaksa za `if` in `elseif` izjave je enaka kot v PHP. Z njimi lahko kličete
funkcije, metode, uporabljate konstante in izvajate aritmetične operacije.
Spremenljivke so prevedene v lokalne spremenljivke, katere ste določili pri
prikazu template datoteke. Istočasno se lahko uporablja PHP sintakso za
naslavljanje množic kot pa tudi olajšano (shorthand) sintakso z uporabo pike.
V prvem primeru je demonstrirana različna sintaksa za množice.
#### 3.1. if izjave
~~~~~~~~~~~~~
{if $is.admin && $user['name']=="black"}
{* Hello black! Only you can edit things,
but only as long as you stay an admin. *}
...
{/if}
~~~~~~~~~~~~~
#### 3.2. if/elseif/else izjave
~~~~~~~~~~~~~
{if $is.admin && $user['name']=="black"}
{* Hello black! Only you can edit things,
but only as long as you stay an admin. *}
...
{elseif $is_moderator}
{* Hello moderator! You can do some things. *}
{else}
{* You are a nobody and you earn nothing! *}
{/if}
~~~~~~~~~~~~~
#### 3.3. foreach/else izjave
~~~~~~~~~~~~~
{foreach $newsitems.$section.items as $item}
<div class="newsitem">
<h3 class="title">{item.title}</h3>
<div class="content">{item.content}</div>
</div>
{else}
<div class="notice">
No newsitems exist in the chosen section.
</div>
{/if}
~~~~~~~~~~~~~
#### 3.4. nocache
MiniTPL template datoteke se prevajajo v PHP datoteke po potrebi
za najboljšo možno hitrost izvajanja. Z uporabo `{include}` direktiv
se osveževanje php datotek rahlo zakomplicira. Za potrebe razvoja smo
dodali `nocache` direktivo. Z uporabo direktive zagotovite, da se vaš
template cache zbriše po vsaki uporabi.
~~~~~~~~~~~~
{*nocache*}
~~~~~~~~~~~~
To funkcionalnost se mora omogočiti z setiranjem `$tpl->_nocache` na `true`.
<h3 id="blocks">4. Bloki in Inline definicije <a href="#top">Δ Skok na vrh</a></h3>
Odvisno od uporabe boste morda želeli večkrat uporabiti delce iste kode v
eni ali večih template datotekah. To lahko dosežete z uporabo `block` in
`inline` definicij. Razlika med `block` definicijo in `inline` definicijo je
v tem, da se `block` definicija prevede v PHP funkcijo in jo lahko uporabljamo
rekurzivno, naprimer za ustvarjanje drevesne strukture. Uporaba `inline`
definicije vam omogoča le večkratno uporabo enake kode.
#### 4.1 block definicija in uporaba
~~~~~~~~~~~
{block recurse}
{if $i++ < 10}
call {i}
{block:recurse}
{/if}
{/block}
{block:recurse}
~~~~~~~~~~~
Prevedena template datoteka bo izgledala nekako takole:
~~~~~~~~~~~
<?php
/* This code has been cleaned up some, for
documentation purposes. It illustrates
the compile aspect of blocks, but is not
a carbon copy of the compiled template. */
$_v = &$this->vars;
function recurse_1213891842_673($_v) {
if ($_v['i']++ < 10) {
echo "call ".$_v['i'];
recurse_1213891842_673(&$_v);
}
}
recurse_1213891842_673(&$_v);
?>
~~~~~~~~~~~
Kot lahko vidite, se `block` definicija prevede v funkcijo, ki uporablja
iste spremenljivke, katere ste določili template datoteki.
Ustvarjanje ali izris drevesne strukture ta način ni zelo praktičen, je pa
možen. Vrjetno nikoli ne boste potrebovali tega. Po naših izkušnjah je
rekurzija uporabljena le redko, če pa že, pa v PHP kodi in ne v template
datotekah.
Uporabite lahko `block` konstrukt namesto `inline`, če vas skrbi za velikost
prevedene template datoteke. Sam bi to predlagal, ko pridete do tega, da je
ena `inline` definicija dolga več kilobyteov in se jo uporablja večkrat v
isti template datoteki.
#### 4.2 inline definicija in uporaba
Ključna beseda `inline` prihaja iz programskega jezika `C++`, kjer prevajalnik
zamenja klice inline funkcij z vsebino funkcije. S tem v `C++` pridobimo na
hitrosti, ker je klicanje funkcije bolj praktično za berljivost, kot pa
kopiranje kode po najvišjem nivoju. Iz enakega razloga se uporablja tudi
v tem template sistemu.
~~~~~~~~~~
{inline newsitem}
<div class="news">
<h3 class="title">{news.title}</h3>
<div class="content">{news.content}</div>
</div>
{/inline}
<div class="top_news">
{foreach $newsitems.top.items as $news}{inline:newsitem}{/foreach}
</div>
<div class="other_news">
{foreach $newsitems.$section.items as $news}{inline:newsitem}{/foreach}
</div>
~~~~~~~~~~
Uporaba `inline` definicij zmanjša velikost template datotek pred prevajanjem
in pripomore k lepši obliki template datotek. Določeni sestavni deli so lahko
torej večkrat uporabljeni v template datoteki.
<h3 id="advanced">5. Napredno, vstavljanje PHP kode <a href="#top">Δ Skok na vrh</a></h3>
Medtem ko zgornji jezikovni sestavki poskrbijo le za izpis, včasih potrebujemo
tudi posodobiti spremenljivke znotraj template datoteke. Za to poskrbijo
sestavki `eval`, `eval_literal` in `php`. Sestavek `php` je namenjen
zahtevnejšim operacijam in ima kot `eval` dostop direktno do spremenljivk
katere ste podali template datoteki.
#### 5.1 eval
Eval sestavek vam omogoča hitre operacije z lokalnimi template spremenljivkami.
Če želite naprimer narediti tabelo, ki ima v vsaki vrstici drugačen CSS stil,
bi to naredili nekako takole:
~~~~~~~~~~~~~
{eval $style="even";}
<table>
{foreach $rows as $row}
{eval $style = ($style=="even") ? "odd" : "even"}
<tr class="{style}">
<td>{row.message}</td>
</tr>
{/foreach}
</table>
~~~~~~~~~~~~~
Uporabo spremenljivke `$_v` odsvetujemo, saj lahko povzročajo napake
pri prevajanju, ali pa z uporabo te spremenljivke prepišete podatke, kateri
so bili namenjeni prikazu.
Uporaba `{` in `}` znotraj `eval` in `eval_literal` sestavkov ni mogoča.
#### 5.2 eval_literal
Ko potrebujete za izvajanje kode globalne spremenljivke ali objekte,
lahko uporabite `eval_literal` sestavek. Koda znotraj sestavka se ne prevede,
ostane torej taka kot je.
~~~~~~~~~~~
{eval_literal
global $cms_module;
$_v['menu_data'] =
$cms_module->get_menu("branch", array("item","menu")); }
~~~~~~~~~~~
Uporaba `{` in `}` znotraj `eval` in `eval_literal` sestavkov ni mogoča.
#### 5.3 php
~~~~~~~~~~~~~~~
{php}
function mygettime()
{
return array("time"=>time(),"date"=>date("r"),"microtime"=>microtime());
}
$mygettime = mygettime();
{/php}
{mygettime|var_dump}
~~~~~~~~~~~~~~~
Zgornja koda se prevede v:
~~~~~~~~~
<?php
$_v = &$this->vars;
function mygettime()
{
return array("time"=>time(),"date"=>date("r"),"microtime"=>microtime());
}
$_v['mygettime'] = mygettime();
echo var_dump($_v['mygettime']);
?>
~~~~~~~~~
To je najmanj uporabljen in najmanj testiran sestavek. Če želite zgraditi
objekte ali funkcije ali uporabiti veliko PHP kode znotraj tamplate datoteke,
definitivno delate nekaj narobe, čeprav template sistem to podpira.
Uporabo spremenljivke `$_v` odsvetujemo, saj lahko povzročajo napake
pri prevajanju, ali pa z uporabo te spremenljivke prepišete podatke, kateri
so bili namenjeni prikazu.
<h3 id="php">6. Uporaba template sistema v PHP <a href="#top">Δ Skok na vrh</a></h3>
V tej kategoriji bomo pregledali zmogljivosti template sistema v PHP.
Ker že znate izdelovati template datoteke vam bomo v tem oddelku prikazali,
kako narediti PHP kodo s katero uporabimo template datoteke.
#### 6.1 Osnovna uporaba
Za osnovno uporabo morate vključiti datoteko `class.template.php`. Uporabljene
funkcije bodo po potrebi same vključile datoteko `class.template_compiler.php`.
Za razvijalce so na voljo naslednje metode:
`load`, `assign`, `render`, `get`, `set_paths`, `compile`
Ponavadi boste uporabljali le prve štiri metode, če seveda ne želite
spremeniti map, kjer sistem išče template datoteke ali pa bi želeli sami
prevajati template datoteke.
##### 6.1. load ( string $filename )
S to metodo določite katera template datoteka se naj naloži. Metoda bo
preiskala nastavljene poti (Privzeto: `templates/`) in naložila želeno
template datoteko. Klic te metode izprazni podatke namenjene template
datoteki, katerega naknadno določite z uporabo `assign` metode.
##### 6.2. assign ( mixed $key, [ mixed $value = '' ] )
Za programerja je ta metoda najbolj ključna. Rezultat metode je različen glede
na količino in vrstni red podanih parametrov.
Če je prvi parameter množica, drug parameter pa ni podan, potem se template
datoteki določi ena vrednost z ključem vnosa v množico, za vsak vnos.
Če je prvi parameter množica in drug parameter niz znakov, bo narejen vnos
za vsako vrednost v množici. Za ključ vnosa bo uporabljen niz v drugem
parametru, kateremu sledi `_` in potem ključ vnosa iz množice.
Če je prvi parameter niz znakov, potem bo drug parameter določen kot vrednost
dosegljiva pod ključem prvega parametra. Tip drugega parametra ni pomemben.
~~~~~~~~
$data = array(); // some example data
$data['title'] = "Leno promises smooth transition to O'Brien";
$data['content'] = "For months, Fallon has been widely considered
the top choice to succeed OBrien when he steps
down next year. On Thursday, published reports ...";
/* This will define an entry {timestamp} */
$tpl->assign("timestamp", time());
/* This will define entries {title} and {content} */
$tpl->assign($data);
/* This example will define {news_title} and {news_content} */
$tpl->assign($data, "news");
/* This will define the item {news}, which contains an array.
You can output the fields with {news.title} and {news.content} */
$tpl->assign("news", $data);
~~~~~~~~
##### 6.3. render, get
Te metode nimajo parametrov. Prevajanje template datotek je izvedeno v ozadju,
če je potrebno. Metoda `render` izpiše podatke v navadni obliki, medtem ko
jih medota `get` vrne v obliki katero lahko uporabimo v PHP.
##### 6.4. set_paths ( [ $paths = false ] )
Metoda `set_paths` vzame množico z možnimi lokacijami za template datoteke.
Lokacija za prevajanje template datotek je mapa `cache`, katera mora obstajati
v vsaki od podanih map. Podane mape se morajo končati z vrezom (slash).
Template sistem bo pregledal `$paths` množico, dokler ne najde template
datoteke, katero želimo naložiti preko metode `load`. Če datoteke ne najde,
izpiše napako, izvajanje se konča.
Metoda je uporabna v primeru, če želite imeti več map s template datotekami.
Naprimer, če želite imeti CMS strukturo.
~~~~~~~~~~~~~~~
$paths = array();
/* This is the most important location, everything
can be overriden from inside the theme. */
$paths[] = "theme/templates/";
/* This is the second most important location,
it usually defines the look of the cms modules */
$paths[] = "modules/".$module_name."/templates/";
/* This is the least important template location,
it usually provides system wide templates, like
a paginator template, an xml / rss template, or
other very general templates. */
$paths[] = "include/templates/";
$tpl->set_paths($paths);
~~~~~~~~~~~~~~~
Konstrukt `{include}` v template datotekah upošteva nastavitve podane preko
metode `set_paths`. Pred prevajanjem sistem išče datoteke za vključitev na
istih lokacijah, dokler jih ne najde.

View File

@@ -0,0 +1,22 @@
<phpunit bootstrap="test/bootstrap.php" colors="true">
<testsuites>
<testsuite name="MiniTPL for PHP - Test Suite">
<file>test/TemplateTest.php</file>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">code</directory>
<exclude>
<directory suffix=".php">vendor</directory>
</exclude>
</whitelist>
<blacklist>
<directory suffix=".php">vendor</directory>
<file>/usr/local/bin/phpunit</file>
</blacklist>
</filter>
<logging>
<log type="coverage-html" target="./test/coverage" highlight="true"/>
</logging>
</phpunit>

View File

@@ -0,0 +1 @@
coverage

View File

@@ -0,0 +1,128 @@
<?php
class TemplateTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider compileProvider
*/
public function testCompile($template)
{
global $tpl;
$destination = "test/compile/".$template;
$compiled = "test/compiled/".$template;
$tpl = new Monotek\MiniTPL\Template;
$tpl->set_paths("test/templates/");
$tpl->set_compile_location("test/compile/", false);
$tpl->add_default("key", "val");
$source = "test/templates/".$template;
$return = $tpl->compile($source, $destination);
$this->assertTrue((bool)$return);
$this->assertFileEquals($destination, $compiled);
}
public function compileProvider()
{
$tests = array();
$templates = glob("test/templates/*.tpl");
sort($templates);
foreach ($templates as $template) {
$tests[] = array(basename($template));
}
return $tests;
}
public function testRendering()
{
$tpl = new Monotek\MiniTPL\Template;
$tpl->set_paths("test/templates/");
$tpl->set_compile_location("test/compile/", false);
$tpl->add_default("key", "val");
$retval = array();
exec("rm -rf test/templates/test -rf", $retval);
$this->assertTrue($tpl->load("08_utf8_bom.tpl"));
touch("test/templates/test/compile/08_utf8_bom.tpl", filemtime("test/templates/08_utf8_bom.tpl") - 86400);
$this->assertTrue($tpl->load("08_utf8_bom.tpl"));
$items = array();
$items[] = array("id" => $i++);
$items[] = array("id" => $i++);
$items[] = array("id" => $i++);
$tpl->assign("items", $items);
$tpl->assign(array("foo"=>"bar", "d" => array("burger")), "foo");
$tpl->assign(".foo_foo", "baz");
$tpl->assign(".foo_d", array("steak", "beef", "pork", "chicken"));
$contents1 = $tpl->get();
ob_start();
$tpl->render();
$contents2 = ob_get_contents();
ob_end_clean();
$this->assertEquals($contents1, $contents2);
$this->assertFalse($tpl->_find_path("404.tpl"));
$tpl->set_compile_location("test/compile/", false);
$this->assertEquals("test/templates/test/compile/", $tpl->_compile_path("test/templates/"));
$tpl->set_compile_location("/test/compile/", true);
$this->assertEquals("/test/compile/test/templates/", $tpl->_compile_path("test/templates/"));
$retval = array();
exec("rm -rf test/templates/test -rf", $retval);
}
public function testException()
{
$this->setExpectedException("Exception");
$tpl = new Monotek\MiniTPL\Template;
$tpl->set_compile_location("test/compile/", false);
$tpl->set_paths("test/templates2/");
$tpl->load("fail_to_compile.tpl");
}
/**
* @dataProvider varsProvider
*/
public function testVars($expression, $expected, $description)
{
global $tpl;
$tpl = new Monotek\MiniTPL\Compiler;
$result = $tpl->_split_exp($expression);
$this->assertEquals($expected, $result);
}
public function testFailure()
{
$this->setExpectedException("Exception");
$tpl = new Monotek\MiniTPL\Template;
$this->assertFalse($tpl->load("missing.tpl"));
$tpl->render();
}
public function varsProvider()
{
$vars = array();
$vars[] = array('news_section_news_list.tpl', 'news_section_news_list.tpl', "normal string");
$vars[] = array('$var', "\$_v['var']", "variable");
$vars[] = array('$var.netko', "\$_v['var']['netko']", "array index");
$vars[] = array('$var . "netko"', "\$_v['var'] . \"netko\"", "string concat");
$vars[] = array('$var1 . $var2', "\$_v['var1'] . \$_v['var2']", "variable concat");
$vars[] = array('$var1.$var2', "\$_v['var1'][\$_v['var2']]", "array var index");
$vars[] = array('$items.0', "\$_v['items']['0']", "array int index");
$vars[] = array('$tpl->get()', "\$tpl->get()", "global function");
$vars[] = array('$tplx->get()', "\$_v['tplx']->get()", "object function");
return $vars;
}
}

View File

@@ -0,0 +1,7 @@
<?php
error_reporting(E_ALL^E_NOTICE);
require_once 'vendor/autoload.php';
@mkdir("test/compile");

View File

@@ -0,0 +1,16 @@
<?php $_v=&$this->vars;?>
#### 1.1. Variables
Using variables from templates is easy. Variables are enclosed
in curly braces like so: `<?php echo $_v['variable'];?>
`. Since variables are parsed
on the last stage, prefixing variables with the dollar sign is
optional. So, `<?php echo $_v['variable'];?>
` is the same as `<?php echo $_v['variable'];?>
`.
~~~~~~~~~~~~~
<?php echo $_v['variable'];?>
is the same as <?php echo $_v['variable'];?>
~~~~~~~~~~~~~

View File

@@ -0,0 +1,18 @@
<?php $_v=&$this->vars;?>
#### 1.2. Arrays
There is a shorthand syntax for using arrays inside a template.
The array index separator is a dot (`.`). Consecutive dots can be
used for traversing into array depth like `<?php echo $_v['array']['items']['0'];?>
`, or
even variables starting with `$`, like `<?php echo $_v['sections'][$_v['news_section']]['title'];?>
`.
PHP syntax for arrays is also supported.
~~~~~~~~~~~~
<?php echo $_v['array']['items'];?>
is the same as <?php echo $_v['array']['items'];echo $_v['array']['items']['0'];?>
is the same as <?php echo $_v['array']['items'][0];echo $_v['array'][$_v['items']]['0'];?>
is the same as <?php echo $_v['array'][$_v['items']][0];?>
~~~~~~~~~~~~

View File

@@ -0,0 +1,44 @@
<?php $_v=&$this->vars;?>
#### 1.3. Modifiers
Modifiers are PHP functions, which take one arbitrary value
(usually, string), and return a string, which gets shown.
Some functions in PHP can be used as modifiers
(strtoupper, ucfirst, str_rot13, strrev, count, ...).
~~~~~~~~~~~~~~~~~~~~~~~~
/** Custom modifier example */
function add_it_up($array)
{
$size = 0;
foreach ($array as $value) {
$size += $value['size'];
}
return $size;
}
~~~~~~~~~~~~~~~~~~~~~~~~
To use the above function in a template, just do `<?php echo add_it_up($_v['variable']);?>
`,
which will loop trough all `$variable` items and add up the value of
the size element and return the total sum of all sizes.
~~~~~~~~~~~~~~
<?php echo htmlspecialchars($_v['variable'], ENT_QUOTES);echo strtoupper($_v['variable']);echo add_id_up($_v['variable']);?>
~~~~~~~~~~~~~~
The above code gets compiled to:
~~~~~~~~~~~~~~
<?php
$_v = &$this->vars;
echo htmlspecialchars($_v['variable'], ENT_QUOTES);
echo strtoupper($_v['variable']);
echo add_id_up($_v['variable']);
?>
~~~~~~~~~~~~~~
A common use for modifiers is outputting data for javascript,
using the php function `json_encode`.

View File

@@ -0,0 +1,56 @@
<?php $_v=&$this->vars;?>
#### 1.4. Objects
You can also call object methods as modifiers, from global or
local objects. The compiler will determine at run-time, if
you have a global object by the name, and modify the code
accordingly.
~~~~~~~~~~~
<?php echo $memcache->get($_v['variable']);?>
~~~~~~~~~~~
If the global object doesn't exist, it assumes a local object.
~~~~~~~~~~~
<?php
$_v = &$this->vars;
echo $_v['memcache']->get($_v['variable']);
?>
~~~~~~~~~~~
However, if a global object by the name $memcache exists at
compile time:
~~~~~~~~~~
<?php
$_v = &$this->vars;
global $memcache;
echo $memcache->get($_v['variable']);
?>
~~~~~~~~~~
You can also use variables from objects, in the same way.
~~~~~~~~~~~~
<?php echo $_v['memcache']->variable;?>
~~~~~~~~~~~~
Compiles to one of theese:
~~~~~~~~~~
<?php
$_v = &$this->vars;
echo $_v['memcache']->variable;
?>
~~~~~~~~~~
~~~~~~~~~~
<?php
$_v = &$this->vars;
global $memcache;
echo $memcache->variable;
?>
~~~~~~~~~~

View File

@@ -0,0 +1,15 @@
<?php $_v=&$this->vars;?>
#### 1.5. Constants
Value starting with `_` is assumed to be a constant. The template
system will output the value of the constant if defined,
or just the name of the constant. Item `<?php echo _MY_CONSTANT;?>
` will be
used as a constant, because of those rules, however `<?php echo $_v['_my_variable'];?>
`
wouldnt be, since it starts with the variable identifier `$`.
~~~~~~~~~~~
<?php echo _MY_CONSTANT;echo _this_is_also_a_constant;echo $_v['_my_variable'];?>
~~~~~~~~~~~

View File

@@ -0,0 +1,17 @@
<?php $_v=&$this->vars;?>
#### 1.6. Includes
#### 1.5. Constants
Value starting with `_` is assumed to be a constant. The template
system will output the value of the constant if defined,
or just the name of the constant. Item `<?php echo _MY_CONSTANT;?>
` will be
used as a constant, because of those rules, however `<?php echo $_v['_my_variable'];?>
`
wouldnt be, since it starts with the variable identifier `$`.
~~~~~~~~~~~
<?php echo _MY_CONSTANT;echo _this_is_also_a_constant;echo $_v['_my_variable'];?>
~~~~~~~~~~~

View File

@@ -0,0 +1,12 @@
<?php $_v=&$this->vars; $_v['key']="nu";?>
<?php $_v['i']=1;if(!empty($_v['items']))foreach($_v['items'] as $_v['index'] => $_v['item']){?>
<?php $_v['id'] = $_v['key'] . $_v['i'];?>
<?php if($_v['i']>1){ echo $_v['i'];?>
nd<?php }else{?>
first<?php }?>
<?php $_v['i']++;}?>

View File

@@ -0,0 +1,8 @@
<?php $_v=&$this->vars;?>
This file contains utf8 BOM.
It also uses the <?php echo $_v['ldelim'];?>
load<?php echo $_v['rdelim'];?>
construct:
<?php $this->push();$this->load("07_eval.tpl");$this->assign($_v);$this->render();$this->pop();?>

View File

@@ -0,0 +1,18 @@
<?php $_v=&$this->vars;?>
Functions bro, functions?
<?php $_v['i']=1;if (!function_exists('function_call_95751455_2926125812')) { function function_call_95751455_2926125812($_v) {?>
This is a created function. This is the <?php echo $_v['i']++;?>
st/nd/rd/th time calling this method.
<?php if($_v['i'] <= 5){function_call_95751455_2926125812(&$_v);}?><?php } }?>
This is like a copy-paste over your <?php echo __FILE__;?>
, anywhere you use this construct.
This is like a copy-paste over your <?php echo __FILE__;?>
, anywhere you use this construct.
<?php function_call_95751455_2926125812(&$_v);?>

View File

@@ -0,0 +1,23 @@
<?php $_v=&$this->vars;?>
The following block is literal (no template constructs work inside the tags)
<script type="text/template">
You can go {nuts} in here, no variables/etc will be parsed.
Except for <?php echo _CONSTANTS;?>
, those will work.
</script>
Or this:
<script type="text/x-jquery">
You can go {nuts} in here, no variables/etc will be parsed.
</script>
But this works:
<script type="text/javascript">
// var some_object = <?php echo $_v['hello_this_is_a_variable'];?>
;
</script>

View File

@@ -0,0 +1,15 @@
<?php $_v=&$this->vars; for($_v['i']=99; $_v['i']>=1; $_v['i']--){echo $_v['i'];?>
bottles of beer on the wall, <?php echo $_v['i'];?>
bottles of beer. Take one down and pass it around, <?php echo $_v['i']-1;?>
bottles of beer on the wall.
<?php }if($_v['i'] > 10){?>
We have some beer left.
<?php }elseif($_v['i'] > 3){?>
Our beer is going to run out.
<?php }else{?>
Critical! Only <?php echo $_v['i'];?>
beer left.
<?php }?>

View File

@@ -0,0 +1,9 @@
<?php $_v=&$this->vars; global $tpl;?>
There is a distinct difference between
calling `$tpl->get` or `$tplx->get`.
When `$tpl` is a global variable, the template engine detects this.
<?php echo $tpl->_paths;?>
<?php echo $_v['tplx']->_paths;?>
;

View File

@@ -0,0 +1,7 @@
<?php $_v=&$this->vars;?>
We check if arrays are empty to suppress warnings/notices.
<?php foreach(array("GET", "POST") as $_v['method']){?>
HTTP <?php echo $_v['method'];}foreach(array("GET", "POST") as $_v['method']){?>
HTTP <?php echo $_v['method'];}if(!empty($_v['methods']))foreach($_v['methods'] as $_v['method']){?>
HTTP <?php echo $_v['method'];}?>

View File

@@ -0,0 +1,10 @@
#### 1.1. Variables
Using variables from templates is easy. Variables are enclosed
in curly braces like so: `{variable}`. Since variables are parsed
on the last stage, prefixing variables with the dollar sign is
optional. So, `{$variable}` is the same as `{variable}`.
~~~~~~~~~~~~~
{variable} is the same as {$variable}
~~~~~~~~~~~~~

View File

@@ -0,0 +1,13 @@
#### 1.2. Arrays
There is a shorthand syntax for using arrays inside a template.
The array index separator is a dot (`.`). Consecutive dots can be
used for traversing into array depth like `{$array.items.0}`, or
even variables starting with `$`, like `{$sections.$news_section.title}`.
PHP syntax for arrays is also supported.
~~~~~~~~~~~~
{$array.items} is the same as {$array['items']}
{$array.items.0} is the same as {$array['items'][0]}
{$array.$items.0} is the same as {$array[$items][0]}
~~~~~~~~~~~~

View File

@@ -0,0 +1,43 @@
#### 1.3. Modifiers
Modifiers are PHP functions, which take one arbitrary value
(usually, string), and return a string, which gets shown.
Some functions in PHP can be used as modifiers
(strtoupper, ucfirst, str_rot13, strrev, count, ...).
~~~~~~~~~~~~~~~~~~~~~~~~
/** Custom modifier example */
function add_it_up($array)
{
$size = 0;
foreach ($array as $value) {
$size += $value['size'];
}
return $size;
}
~~~~~~~~~~~~~~~~~~~~~~~~
To use the above function in a template, just do `{$variable|add_it_up}`,
which will loop trough all `$variable` items and add up the value of
the size element and return the total sum of all sizes.
~~~~~~~~~~~~~~
{variable|escape}
{variable|toupper}
{variable|add_id_up}
~~~~~~~~~~~~~~
The above code gets compiled to:
~~~~~~~~~~~~~~
<?php
$_v = &$this->vars;
echo htmlspecialchars($_v['variable'], ENT_QUOTES);
echo strtoupper($_v['variable']);
echo add_id_up($_v['variable']);
?>
~~~~~~~~~~~~~~
A common use for modifiers is outputting data for javascript,
using the php function `json_encode`.

View File

@@ -0,0 +1,53 @@
#### 1.4. Objects
You can also call object methods as modifiers, from global or
local objects. The compiler will determine at run-time, if
you have a global object by the name, and modify the code
accordingly.
~~~~~~~~~~~
{$variable|$memcache->get}
~~~~~~~~~~~
If the global object doesn't exist, it assumes a local object.
~~~~~~~~~~~
<?php
$_v = &$this->vars;
echo $_v['memcache']->get($_v['variable']);
?>
~~~~~~~~~~~
However, if a global object by the name $memcache exists at
compile time:
~~~~~~~~~~
<?php
$_v = &$this->vars;
global $memcache;
echo $memcache->get($_v['variable']);
?>
~~~~~~~~~~
You can also use variables from objects, in the same way.
~~~~~~~~~~~~
{$memcache->variable}
~~~~~~~~~~~~
Compiles to one of theese:
~~~~~~~~~~
<?php
$_v = &$this->vars;
echo $_v['memcache']->variable;
?>
~~~~~~~~~~
~~~~~~~~~~
<?php
$_v = &$this->vars;
global $memcache;
echo $memcache->variable;
?>
~~~~~~~~~~

View File

@@ -0,0 +1,13 @@
#### 1.5. Constants
Value starting with `_` is assumed to be a constant. The template
system will output the value of the constant if defined,
or just the name of the constant. Item `{_MY_CONSTANT}` will be
used as a constant, because of those rules, however `{$_my_variable}`
wouldnt be, since it starts with the variable identifier `$`.
~~~~~~~~~~~
{_MY_CONSTANT}
{_this_is_also_a_constant}
{$_my_variable}
~~~~~~~~~~~

View File

@@ -0,0 +1,3 @@
#### 1.6. Includes
{include 05_constants.tpl}

View File

@@ -0,0 +1,12 @@
{eval $key="nu"}
{inline item}
{if $i>1}{i}nd{else}first{/if}
{/inline}
{eval $i=1}
{foreach $items as $index => $item}
{eval $id = $key . $i}
{inline:item}
{eval $i++}
{/foreach}

View File

@@ -0,0 +1,5 @@
This file contains utf8 BOM.
It also uses the {ldelim}load{rdelim} construct:
{load 07_eval.tpl}

View File

@@ -0,0 +1,19 @@
{inline snippet1}
This is like a copy-paste over your {eval echo __FILE__}, anywhere you use this construct.
{/inline}
Functions bro, functions?
{eval $i=1}
{block function_call}
This is a created function. This is the {i++}st/nd/rd/th time calling this method.
{if $i <= 5}
{block:function_call}
{/if}
{/block}
{inline:snippet1}
{inline:snippet1}
{block:function_call}

View File

@@ -0,0 +1,20 @@
The following block is literal (no template constructs work inside the tags)
<script type="text/template">
You can go {nuts} in here, no variables/etc will be parsed.
Except for {_CONSTANTS}, those will work.
</script>
Or this:
<script type="text/x-jquery">
You can go {nuts} in here, no variables/etc will be parsed.
{* And comments get removed also *}
</script>
But this works:
<script type="text/javascript">
// var some_object = {hello_this_is_a_variable};
</script>

View File

@@ -0,0 +1,11 @@
{for $i=99; $i>=1; $i--}
{i} bottles of beer on the wall, {i} bottles of beer. Take one down and pass it around, {eval echo $i-1} bottles of beer on the wall.
{/for}
{if $i > 10}
We have some beer left.
{elseif $i > 3}
Our beer is going to run out.
{else}
Critical! Only {i} beer left.
{/if}

View File

@@ -0,0 +1,6 @@
There is a distinct difference between
calling `$tpl->get` or `$tplx->get`.
When `$tpl` is a global variable, the template engine detects this.
{$tpl->_paths} {$tplx->_paths};

View File

@@ -0,0 +1,13 @@
We check if arrays are empty to suppress warnings/notices.
{foreach array("GET", "POST") as $method}
HTTP {method}
{/foreach}
{foreach (array("GET", "POST") as $method)}
HTTP {method}
{/foreach}
{foreach $methods as $method}
HTTP {method}
{/foreach}