This commit is contained in:
Konstantin Pavlov 2013-09-18 11:35:12 -07:00
commit d39002e4c7
37 changed files with 585 additions and 432 deletions

5
.gitignore vendored
View file

@ -20,4 +20,7 @@ themes/*
!themes/default/*
# User config
config.php
config.php
*.iml
*.sublime-*
.idea

View file

@ -1,5 +1,14 @@
*** Pico Changelog ***
2013.07.21 - version 0.7a2
* [new] Added ability to use custom meta data
* [new] Added ability to choose custom template per page
* [changed] Removed closing php tags from files
* [changed] Now managing markdown parser via composer
* [changed] Updated Twig version
* [changed] get_files no longer gets dotfiles
* [fixed] Issues with updating Pico install once in use
2013.09.04 - version 0.7
* [New] Added before_read_file_meta and get_page_data plugin hooks to customize page meta data
* [Changed] Make get_files() ignore dotfiles

View file

@ -1,6 +1,6 @@
{
"require": {
"twig/twig": "1.12.*",
"twig/twig": "1.*",
"michelf/php-markdown": "1.3"
}
}

View file

@ -15,9 +15,12 @@ $config['twig_config'] = array( // Twig settings
$config['pages_order_by'] = 'alpha'; // Order pages by "alpha" or "date"
$config['pages_order'] = 'asc'; // Order pages "asc" or "desc"
$config['excerpt_length'] = 50; // The pages excerpt length (in words)
$config['template_ext'] = '.html'; // Template file extension
// To add a custom config setting:
$config['custom_setting'] = 'Hello'; // Can be accessed by {{ config.custom_setting }} in a theme
*/
*/
// End of file

View file

@ -1,6 +1,7 @@
/*
Title: Welcome
Description: This description will go in the meta description tag
keywords: Pico CMS
*/
## Welcome to Pico
@ -43,6 +44,8 @@ At the top of text files you can place a block comment and specify certain attri
Author: Joe Bloggs
Date: 2013/01/01
Robots: noindex,nofollow
Keywords: keyword1, keyword2
Foo: Bar
*/
These values will be contained in the `{{ meta }}` variable in themes (see below).
@ -65,13 +68,15 @@ All themes must include an `index.html` file to define the HTML structure of the
* `{{ theme_dir }}` - The path to the Pico active theme direcotry
* `{{ theme_url }}` - The URL to the Pico active theme direcotry
* `{{ site_title }}` - Shortcut to the site title (defined in config.php)
* `{{ meta }}` - Contains the meta values from the current page
* `{{ meta }}` - Contains the meta values from the current page. Meta keys are always converted to lowercase.
* `{{ meta.title }}`
* `{{ meta.description }}`
* `{{ meta.author }}`
* `{{ meta.date }}`
* `{{ meta.date_formatted }}`
* `{{ meta.robots }}`
* `{{ meta.keywords }}`
* `{{ meta.foo }}`
* `{{ content }}` - The content of the current page (after it has been processed through Markdown)
* `{{ pages }}` - A collection of all the content in your site
* `{{ page.title }}`

View file

@ -8,6 +8,10 @@ define('PLUGINS_DIR', ROOT_DIR .'plugins/');
define('THEMES_DIR', ROOT_DIR .'themes/');
define('CACHE_DIR', LIB_DIR .'cache/');
date_default_timezone_set('UTC');
require(ROOT_DIR .'vendor/autoload.php');
require(LIB_DIR .'pico.php');
$pico = new Pico();
// End of file

86
lib/FilePageDao.php Normal file
View file

@ -0,0 +1,86 @@
<?php
class FilePageDao implements PageDao
{
/**
* @var Pico
*/
private $pico;
/**
* @param Pico $pico
*/
function __construct($pico)
{
$this->pico = $pico;
}
/**
* @inheritdoc
*/
function get_pages($base_url, $order_by = 'alpha', $order = 'asc', $meta_max_length = 2048, $excerpt_length = 50)
{
global $config;
$pages = $this->pico->get_files(CONTENT_DIR, CONTENT_EXT);
$sorted_pages = array();
$date_id = 0;
foreach ($pages as $key => $page) {
// Skip 404
if (basename($page) == '404' . CONTENT_EXT) {
unset($pages[$key]);
continue;
}
// Ignore Emacs (and Nano) temp files
if (in_array(substr($page, -1), array('~', '#'))) {
unset($pages[$key]);
continue;
}
// Get title and format $page
$page_content = file_get_contents($page, NULL, NULL, 0, $meta_max_length);
$page_meta = $this->pico->read_file_meta($page_content);
$page_content = $this->pico->parse_content($page_content);
$url = str_replace(CONTENT_DIR, $base_url . '/', $page);
$url = str_replace('index' . CONTENT_EXT, '', $url);
$url = str_replace(CONTENT_EXT, '', $url);
$data = array(
'title' => isset($page_meta['title']) ? $page_meta['title'] : '',
'url' => $url,
'author' => isset($page_meta['author']) ? $page_meta['author'] : '',
'date' => isset($page_meta['date']) ? $page_meta['date'] : '',
'date_formatted' => isset($page_meta['date']) ? date($config['date_format'], strtotime($page_meta['date'])) : '',
'content' => $page_content,
'excerpt' => $this->limit_words(strip_tags($page_content), $excerpt_length),
'last_modified' => new DateTime('@' . filemtime($page))
);
// Extend the data provided with each page by hooking into the data array
$this->pico->run_hooks('get_page_data', array(&$data, $page_meta));
if ($order_by == 'date' && isset($page_meta['date'])) {
$sorted_pages[$page_meta['date'] . $date_id] = $data;
$date_id++;
} else $sorted_pages[] = $data;
}
if ($order == 'desc') krsort($sorted_pages);
else ksort($sorted_pages);
return $sorted_pages;
}
/**
* Helper function to limit the words in a string
*
* @param string $string the given string
* @param int $word_limit the number of words to limit to
* @return string the limited string
*/
private function limit_words($string, $word_limit)
{
$words = explode(' ', $string);
return trim(implode(' ', array_splice($words, 0, $word_limit))) . '...';
}
}

View file

@ -1,5 +1,21 @@
<?php
use \Michelf\MarkdownExtra;
use Michelf\MarkdownExtra;
interface PageDao
{
/**
* Returns a list of pages.
*
* @param string $base_url the base URL of the site
* @param string $order_by order by "alpha" or "date"
* @param string $order order "asc" or "desc"
* @param int $meta_max_length Maximum length of page meta section
* @param int $excerpt_length Maximum length of the excerpt in words
* @return array $sorted_pages an array of pages
*/
function get_pages($base_url, $order_by = 'alpha', $order = 'asc', $meta_max_length = 2048, $excerpt_length = 50);
}
/**
* Pico
@ -9,340 +25,293 @@ use \Michelf\MarkdownExtra;
* @license http://opensource.org/licenses/MIT
* @version 0.7
*/
class Pico {
class Pico
{
/**
* @var array
*/
private $plugins;
/**
* @var PageDao
*/
private $page_dao;
private $plugins;
/**
* The constructor carries out all the processing in Pico.
* Does URL routing, Markdown processing and Twig processing.
*/
public function __construct()
{
// Load plugins
$this->load_plugins();
if (!isset($this->page_dao)) {
// If plugin not found - fallback to FilePageDao
@require_once(LIB_DIR . 'FilePageDao.php');
$this->page_dao = new FilePageDao($this);
}
/**
* The constructor carries out all the processing in Pico.
* Does URL routing, Markdown processing and Twig processing.
*/
public function __construct()
{
// Load plugins
$this->load_plugins();
$this->run_hooks('plugins_loaded');
// Get request url and script url
$url = '';
$request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
$script_url = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : '';
$this->run_hooks('plugins_loaded');
// Get our url path and trim the / of the left and the right
if($request_url != $script_url) $url = trim(preg_replace('/'. str_replace('/', '\/', str_replace('index.php', '', $script_url)) .'/', '', $request_url, 1), '/');
$url = preg_replace('/\?.*/', '', $url); // Strip query string
$this->run_hooks('request_url', array(&$url));
// Get request url and script url
$url = '';
$request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
$script_url = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : '';
// Get the file path
if($url) $file = CONTENT_DIR . $url;
else $file = CONTENT_DIR .'index';
// Get our url path and trim the / of the left and the right
if ($request_url != $script_url) $url = trim(preg_replace('/' . str_replace('/', '\/', str_replace('index.php', '', $script_url)) . '/', '', $request_url, 1), '/');
$url = preg_replace('/\?.*/', '', $url); // Strip query string
$this->run_hooks('request_url', array(&$url));
// Load the file
if(is_dir($file)) $file = CONTENT_DIR . $url .'/index'. CONTENT_EXT;
else $file .= CONTENT_EXT;
// Get the file path
if ($url) $file = CONTENT_DIR . $url;
else $file = CONTENT_DIR . 'index';
$this->run_hooks('before_load_content', array(&$file));
if(file_exists($file)){
$content = file_get_contents($file);
} else {
$this->run_hooks('before_404_load_content', array(&$file));
$content = file_get_contents(CONTENT_DIR .'404'. CONTENT_EXT);
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
$this->run_hooks('after_404_load_content', array(&$file, &$content));
}
$this->run_hooks('after_load_content', array(&$file, &$content));
// Load the settings
$settings = $this->get_config();
$this->run_hooks('config_loaded', array(&$settings));
// Load the file
if (is_dir($file)) $file = CONTENT_DIR . $url . '/index' . CONTENT_EXT;
else $file .= CONTENT_EXT;
$meta = $this->read_file_meta($content);
$this->run_hooks('file_meta', array(&$meta));
$content = $this->parse_content($content);
$this->run_hooks('content_parsed', array(&$content));
// Get all the pages
$pages = $this->get_pages($settings['base_url'], $settings['pages_order_by'], $settings['pages_order'], $settings['excerpt_length']);
$prev_page = array();
$current_page = array();
$next_page = array();
while($current_page = current($pages)){
if((isset($meta['title'])) && ($meta['title'] == $current_page['title'])){
break;
}
next($pages);
}
$prev_page = next($pages);
prev($pages);
$next_page = prev($pages);
$this->run_hooks('get_pages', array(&$pages, &$current_page, &$prev_page, &$next_page));
$this->run_hooks('before_load_content', array(&$file));
if (file_exists($file)) {
$content = file_get_contents($file);
} else {
$this->run_hooks('before_404_load_content', array(&$file));
$content = file_get_contents(CONTENT_DIR . '404' . CONTENT_EXT);
header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
$this->run_hooks('after_404_load_content', array(&$file, &$content));
}
$this->run_hooks('after_load_content', array(&$file, &$content));
// Load the theme
$this->run_hooks('before_twig_register');
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem(THEMES_DIR . $settings['theme']);
$twig = new Twig_Environment($loader, $settings['twig_config']);
$twig->addExtension(new Twig_Extension_Debug());
$twig_vars = array(
'config' => $settings,
'base_dir' => rtrim(ROOT_DIR, '/'),
'base_url' => $settings['base_url'],
'theme_dir' => THEMES_DIR . $settings['theme'],
'theme_url' => $settings['base_url'] .'/'. basename(THEMES_DIR) .'/'. $settings['theme'],
'site_title' => $settings['site_title'],
'meta' => $meta,
'content' => $content,
'pages' => $pages,
'prev_page' => $prev_page,
'current_page' => $current_page,
'next_page' => $next_page,
'is_front_page' => $url ? false : true,
);
$this->run_hooks('before_render', array(&$twig_vars, &$twig));
$output = $twig->render('index.html', $twig_vars);
$this->run_hooks('after_render', array(&$output));
echo $output;
}
/**
* Load any plugins
*/
private function load_plugins()
{
$this->plugins = array();
$plugins = $this->get_files(PLUGINS_DIR, '.php');
if(!empty($plugins)){
foreach($plugins as $plugin){
include_once($plugin);
$plugin_name = preg_replace("/\\.[^.\\s]{3}$/", '', basename($plugin));
if(class_exists($plugin_name)){
$obj = new $plugin_name;
$this->plugins[] = $obj;
}
}
}
}
// Load the settings
$settings = $this->get_config();
$this->run_hooks('config_loaded', array(&$settings));
/**
* Parses the content using Markdown
*
* @param string $content the raw txt content
* @return string $content the Markdown formatted content
*/
private function parse_content($content)
{
$content = preg_replace('#/\*.+?\*/#s', '', $content); // Remove comments and meta
$content = str_replace('%base_url%', $this->base_url(), $content);
$content = MarkdownExtra::defaultTransform($content);
$meta = $this->read_file_meta($content);
$this->run_hooks('file_meta', array(&$meta));
$content = $this->parse_content($content);
$this->run_hooks('content_parsed', array(&$content));
return $content;
}
// Get all the pages
$pages = $this->page_dao->get_pages(
$settings['base_url'],
$settings['pages_order_by'],
$settings['pages_order'],
$settings['excerpt_length']);
$prev_page = array();
$current_page = array();
$next_page = array();
while ($current_page = current($pages)) {
if ((isset($meta['title'])) && ($meta['title'] == $current_page['title'])) {
break;
}
next($pages);
}
$prev_page = next($pages);
prev($pages);
$next_page = prev($pages);
$this->run_hooks('get_pages', array(&$pages, &$current_page, &$prev_page, &$next_page));
/**
* Parses the file meta from the txt file header
*
* @param string $content the raw txt content
* @return array $headers an array of meta values
*/
private function read_file_meta($content)
{
global $config;
$headers = array(
'title' => 'Title',
'description' => 'Description',
'author' => 'Author',
'date' => 'Date',
'robots' => 'Robots'
);
// Load the theme
$this->run_hooks('before_twig_register');
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem(THEMES_DIR . $settings['theme']);
$twig = new Twig_Environment($loader, $settings['twig_config']);
$twig->addExtension(new Twig_Extension_Debug());
$twig_vars = array(
'config' => $settings,
'base_dir' => rtrim(ROOT_DIR, '/'),
'base_url' => $settings['base_url'],
'theme_dir' => THEMES_DIR . $settings['theme'],
'theme_url' => $settings['base_url'] . '/' . basename(THEMES_DIR) . '/' . $settings['theme'],
'site_title' => $settings['site_title'],
'meta' => $meta,
'content' => $content,
'pages' => $pages,
'prev_page' => $prev_page,
'current_page' => $current_page,
'next_page' => $next_page,
'is_front_page' => $url ? false : true,
);
// use a custom template if specified Template: [filename] in page meta e.g. Template: spesh to try and use spesh.html in theme folder
$template = ((isset($meta['template']) && file_exists($twig_vars['theme_dir'] . '/' . $meta['template'] . $settings['template_ext'])) ? $meta['template'] . $settings['template_ext'] : 'index' . $settings['template_ext']);
// Add support for custom headers by hooking into the headers array
$this->run_hooks('before_read_file_meta', array(&$headers));
$this->run_hooks('before_render', array(&$twig_vars, &$twig));
$output = $twig->render($template, $twig_vars);
$this->run_hooks('after_render', array(&$output));
echo $output;
}
foreach ($headers as $field => $regex){
if (preg_match('/^[ \t\/*#@]*' . preg_quote($regex, '/') . ':(.*)$/mi', $content, $match) && $match[1]){
$headers[ $field ] = trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $match[1]));
} else {
$headers[ $field ] = '';
}
}
if(isset($headers['date'])) $headers['date_formatted'] = date($config['date_format'], strtotime($headers['date']));
/**
* Load any plugins
*/
private function load_plugins()
{
$this->plugins = array();
$plugins = $this->get_files(PLUGINS_DIR, '.php');
if (!empty($plugins)) {
foreach ($plugins as $plugin) {
include_once($plugin);
$plugin_name = preg_replace("/\\.[^.\\s]{3}$/", '', basename($plugin));
if (class_exists($plugin_name)) {
$obj = new $plugin_name;
$this->plugins[] = $obj;
return $headers;
}
if ($obj instanceof PageDao) {
$this->page_dao = $obj;
}
}
}
}
}
/**
* Loads the config
*
* @return array $config an array of config values
*/
private function get_config()
{
global $config;
@include_once(ROOT_DIR .'config.php');
/**
* Helper function to recusively get all files in a directory
*
* @param string $directory start directory
* @param string $ext optional limit to file extensions
* @return array the matched files
*/
static function get_files($directory, $ext = '')
{
$array_items = array();
if ($handle = opendir($directory)) {
while (false !== ($file = readdir($handle))) {
if (preg_match("/^(^\.)/", $file) === 0) {
if (is_dir($directory . "/" . $file)) {
$array_items = array_merge($array_items, self::get_files($directory . "/" . $file, $ext));
} else {
$file = $directory . "/" . $file;
if (!$ext || strstr($file, $ext)) $array_items[] = preg_replace("/\/\//si", "/", $file);
}
}
}
closedir($handle);
}
return $array_items;
}
$defaults = array(
'site_title' => 'Pico',
'base_url' => $this->base_url(),
'theme' => 'default',
'date_format' => 'jS M Y',
'twig_config' => array('cache' => false, 'autoescape' => false, 'debug' => false),
'pages_order_by' => 'alpha',
'pages_order' => 'asc',
'excerpt_length' => 50
);
/**
* Processes any hooks and runs them
*
* @param string $hook_id the ID of the hook
* @param array $args optional arguments
*/
function run_hooks($hook_id, $args = array())
{
if (!empty($this->plugins)) {
foreach ($this->plugins as $plugin) {
if (is_callable(array($plugin, $hook_id))) {
call_user_func_array(array($plugin, $hook_id), $args);
}
}
}
}
if(is_array($config)) $config = array_merge($defaults, $config);
else $config = $defaults;
/**
* Loads the config
*
* @return array $config an array of config values
*/
private function get_config()
{
global $config;
@include_once(ROOT_DIR . 'config.php');
return $config;
}
/**
* Get a list of pages
*
* @param string $base_url the base URL of the site
* @param string $order_by order by "alpha" or "date"
* @param string $order order "asc" or "desc"
* @return array $sorted_pages an array of pages
*/
private function get_pages($base_url, $order_by = 'alpha', $order = 'asc', $excerpt_length = 50)
{
global $config;
$pages = $this->get_files(CONTENT_DIR, CONTENT_EXT);
$sorted_pages = array();
$date_id = 0;
foreach($pages as $key=>$page){
// Skip 404
if(basename($page) == '404'. CONTENT_EXT){
unset($pages[$key]);
continue;
}
$defaults = array(
'site_title' => 'Pico',
'base_url' => $this->base_url(),
'theme' => 'default',
'date_format' => 'jS M Y',
'twig_config' => array('cache' => false, 'autoescape' => false, 'debug' => false),
'pages_order_by' => 'alpha',
'pages_order' => 'asc',
'excerpt_length' => 50,
'template_ext' => '.html'
);
// Ignore Emacs (and Nano) temp files
if (in_array(substr($page, -1), array('~','#'))) {
unset($pages[$key]);
continue;
}
// Get title and format $page
$page_content = file_get_contents($page);
$page_meta = $this->read_file_meta($page_content);
$page_content = $this->parse_content($page_content);
$url = str_replace(CONTENT_DIR, $base_url .'/', $page);
$url = str_replace('index'. CONTENT_EXT, '', $url);
$url = str_replace(CONTENT_EXT, '', $url);
$data = array(
'title' => isset($page_meta['title']) ? $page_meta['title'] : '',
'url' => $url,
'author' => isset($page_meta['author']) ? $page_meta['author'] : '',
'date' => isset($page_meta['date']) ? $page_meta['date'] : '',
'date_formatted' => isset($page_meta['date']) ? date($config['date_format'], strtotime($page_meta['date'])) : '',
'content' => $page_content,
'excerpt' => $this->limit_words(strip_tags($page_content), $excerpt_length)
);
if (is_array($config)) $config = array_merge($defaults, $config);
else $config = $defaults;
// Extend the data provided with each page by hooking into the data array
$this->run_hooks('get_page_data', array(&$data, $page_meta));
return $config;
}
if($order_by == 'date' && isset($page_meta['date'])){
$sorted_pages[$page_meta['date'].$date_id] = $data;
$date_id++;
}
else $sorted_pages[] = $data;
}
if($order == 'desc') krsort($sorted_pages);
else ksort($sorted_pages);
return $sorted_pages;
}
/**
* Processes any hooks and runs them
*
* @param string $hook_id the ID of the hook
* @param array $args optional arguments
*/
private function run_hooks($hook_id, $args = array())
{
if(!empty($this->plugins)){
foreach($this->plugins as $plugin){
if(is_callable(array($plugin, $hook_id))){
call_user_func_array(array($plugin, $hook_id), $args);
}
}
}
}
/**
* Helper function to work out the base URL
*
* @return string the base url
*/
private function base_url()
{
global $config;
if (isset($config['base_url']) && $config['base_url']) return $config['base_url'];
/**
* Helper function to work out the base URL
*
* @return string the base url
*/
private function base_url()
{
global $config;
if(isset($config['base_url']) && $config['base_url']) return $config['base_url'];
$url = '';
$request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
$script_url = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : '';
if ($request_url != $script_url) $url = trim(preg_replace('/' . str_replace('/', '\/', str_replace('index.php', '', $script_url)) . '/', '', $request_url, 1), '/');
$url = '';
$request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
$script_url = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : '';
if($request_url != $script_url) $url = trim(preg_replace('/'. str_replace('/', '\/', str_replace('index.php', '', $script_url)) .'/', '', $request_url, 1), '/');
$protocol = $this->get_protocol();
return rtrim(str_replace($url, '', $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']), '/');
}
$protocol = $this->get_protocol();
return rtrim(str_replace($url, '', $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']), '/');
}
/**
* Tries to guess the server protocol. Used in base_url()
*
* @return string the current protocol
*/
private function get_protocol()
{
preg_match("|^HTTP[S]?|is", $_SERVER['SERVER_PROTOCOL'], $m);
return strtolower($m[0]);
}
/**
* Tries to guess the server protocol. Used in base_url()
*
* @return string the current protocol
*/
private function get_protocol()
{
preg_match("|^HTTP[S]?|is",$_SERVER['SERVER_PROTOCOL'],$m);
return strtolower($m[0]);
}
/**
* Helper function to recusively get all files in a directory
*
* @param string $directory start directory
* @param string $ext optional limit to file extensions
* @return array the matched files
*/
private function get_files($directory, $ext = '')
{
$array_items = array();
if($handle = opendir($directory)){
while(false !== ($file = readdir($handle))){
if(preg_match("/^(^\.)/", $file) === 0){
if(is_dir($directory. "/" . $file)){
$array_items = array_merge($array_items, $this->get_files($directory. "/" . $file, $ext));
} else {
$file = $directory . "/" . $file;
if(!$ext || strstr($file, $ext)) $array_items[] = preg_replace("/\/\//si", "/", $file);
}
}
}
closedir($handle);
}
return $array_items;
}
/**
* Helper function to limit the words in a string
*
* @param string $string the given string
* @param int $word_limit the number of words to limit to
* @return string the limited string
*/
private function limit_words($string, $word_limit)
{
$words = explode(' ',$string);
return trim(implode(' ', array_splice($words, 0, $word_limit))) .'...';
}
/**
* Parses the file meta from the txt file header.
* Meta keys are converted to lowercase automatically.
*
* @param string $content the raw txt content
* @return array $headers an array of meta values
*/
function read_file_meta($content)
{
global $config;
$headers = array(
'title' => 'Title',
'description' => 'Description',
'author' => 'Author',
'date' => 'Date',
'robots' => 'Robots'
);
// Add support for custom headers by hooking into the headers array
$this->run_hooks('before_read_file_meta', array(&$headers));
foreach ($headers as $field => $regex) {
if (preg_match('/^[ \t\/*#@]*' . preg_quote($regex, '/') . ':(.*)$/mi', $content, $match) && $match[1]) {
$headers[$field] = trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $match[1]));
} else {
$headers[$field] = '';
}
}
if (isset($headers['date'])) $headers['date_formatted'] = date($config['date_format'], strtotime($headers['date']));
return $headers;
}
/**
* Parses the content using Markdown
*
* @param string $content the raw txt content
* @return string $content the Markdown formatted content
*/
function parse_content(&$content)
{
$content = preg_replace('#/\*.+?\*/#s', '', $content); // Remove comments and meta
$content = str_replace('%base_url%', $this->base_url(), $content);
$content = MarkdownExtra::defaultTransform($content);
return $content;
}
}

View file

@ -86,4 +86,4 @@ class Pico_Plugin {
}
?>
// End of file

View file

@ -9,14 +9,15 @@
{% endif %}{% if meta.robots %}
<meta name="robots" content="{{ meta.robots }}">
{% endif %}
{% if meta.keywords %}
<meta name="keywords" content="{{ meta.keywords }}">
{% endif %}
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:400,700" type="text/css" />
<link rel="stylesheet" href="{{ theme_url }}/style.css" type="text/css" />
<script src="{{ theme_url }}/scripts/modernizr-2.6.1.min.js"></script>
</head>
<body>
<header id="header">
<div class="inner clearfix">
<h1><a href="{{ base_url }}">{{ site_title }}</a></h1>

2
vendor/autoload.php vendored
View file

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit68d29614b81b64051229769b084d96b6::getLoader();
return ComposerAutoloaderInit37e27564bfc1364f04889357fcdc05b8::getLoader();

View file

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit68d29614b81b64051229769b084d96b6
class ComposerAutoloaderInit37e27564bfc1364f04889357fcdc05b8
{
private static $loader;
@ -19,9 +19,9 @@ class ComposerAutoloaderInit68d29614b81b64051229769b084d96b6
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit68d29614b81b64051229769b084d96b6', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit37e27564bfc1364f04889357fcdc05b8', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit68d29614b81b64051229769b084d96b6', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit37e27564bfc1364f04889357fcdc05b8', 'loadClassLoader'));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

View file

@ -1,27 +1,27 @@
[
{
"name": "twig/twig",
"version": "v1.12.3",
"version_normalized": "1.12.3.0",
"version": "v1.13.2",
"version_normalized": "1.13.2.0",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Twig.git",
"reference": "v1.12.3"
"reference": "6d6a1009427d1f398c9d40904147bf9f723d5755"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.12.3",
"reference": "v1.12.3",
"url": "https://api.github.com/repos/fabpot/Twig/zipball/6d6a1009427d1f398c9d40904147bf9f723d5755",
"reference": "6d6a1009427d1f398c9d40904147bf9f723d5755",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"time": "2013-04-08 12:40:11",
"time": "2013-08-03 15:35:31",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.12-dev"
"dev-master": "1.13-dev"
}
},
"installation-source": "dist",
@ -32,7 +32,7 @@
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3"
"BSD-3-Clause"
],
"authors": [
{

View file

@ -4,6 +4,7 @@ php:
- 5.2
- 5.3
- 5.4
- 5.5
env:
- TWIG_EXT=no

View file

@ -1,3 +1,25 @@
* 1.13.2 (2013-08-03)
* fixed the error line number for an error occurs in and embedded template
* fixed crashes of the C extension on some edge cases
* 1.13.1 (2013-06-06)
* added the possibility to ignore the filesystem constructor argument in Twig_Loader_Filesystem
* fixed Twig_Loader_Chain::exists() for a loader which implements Twig_ExistsLoaderInterface
* adjusted backtrace call to reduce memory usage when an error occurs
* added support for object instances as the second argument of the constant test
* fixed the include function when used in an assignment
* 1.13.0 (2013-05-10)
* fixed getting a numeric-like item on a variable ('09' for instance)
* fixed getting a boolean or float key on an array, so it is consistent with PHP's array access:
`{{ array[false] }}` behaves the same as `echo $array[false];` (equals `$array[0]`)
* made the escape filter 20% faster for happy path (escaping string for html with UTF-8)
* changed ☃ to § in tests
* enforced usage of named arguments after positional ones
* 1.12.3 (2013-04-08)
* fixed a security issue in the filesystem loader where it was possible to include a template one

View file

@ -4,7 +4,7 @@
"description": "Twig, the flexible, fast, and secure template language for PHP",
"keywords": ["templating"],
"homepage": "http://twig.sensiolabs.org",
"license": "BSD-3",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Fabien Potencier",
@ -25,7 +25,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.12-dev"
"dev-master": "1.13-dev"
}
}
}

View file

@ -18,11 +18,16 @@ class Twig_Autoloader
{
/**
* Registers Twig_Autoloader as an SPL autoloader.
*
* @param Boolean $prepend Whether to prepend the autoloader or not.
*/
public static function register()
public static function register($prepend = false)
{
ini_set('unserialize_callback_func', 'spl_autoload_call');
spl_autoload_register(array(new self, 'autoload'));
if (version_compare(phpversion(), '5.3.0', '>=')) {
spl_autoload_register(array(new self, 'autoload'), true, $prepend);
} else {
spl_autoload_register(array(new self, 'autoload'));
}
}
/**

View file

@ -16,7 +16,7 @@
*/
class Twig_Environment
{
const VERSION = '1.12.3';
const VERSION = '1.13.2';
protected $charset;
protected $loader;
@ -53,7 +53,7 @@ class Twig_Environment
* * debug: When set to true, it automatically set "auto_reload" to true as
* well (default to false).
*
* * charset: The charset used by the templates (default to utf-8).
* * charset: The charset used by the templates (default to UTF-8).
*
* * base_template_class: The base template class to use for generated
* templates (default to Twig_Template).
@ -99,7 +99,7 @@ class Twig_Environment
), $options);
$this->debug = (bool) $options['debug'];
$this->charset = $options['charset'];
$this->charset = strtoupper($options['charset']);
$this->baseTemplateClass = $options['base_template_class'];
$this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
$this->strictVariables = (bool) $options['strict_variables'];
@ -566,7 +566,7 @@ class Twig_Environment
*/
public function setCharset($charset)
{
$this->charset = $charset;
$this->charset = strtoupper($charset);
}
/**
@ -728,7 +728,7 @@ class Twig_Environment
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
{
if ($this->extensionInitialized) {
throw new LogicException('Unable to add a node visitor as extensions have already been initialized.', $extension->getName());
throw new LogicException('Unable to add a node visitor as extensions have already been initialized.');
}
$this->staging->addNodeVisitor($visitor);
@ -1099,10 +1099,17 @@ class Twig_Environment
{
$globals = array();
foreach ($this->extensions as $extension) {
$globals = array_merge($globals, $extension->getGlobals());
$extGlob = $extension->getGlobals();
if (!is_array($extGlob)) {
throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension)));
}
$globals[] = $extGlob;
}
return array_merge($globals, $this->staging->getGlobals());
$globals[] = $this->staging->getGlobals();
return call_user_func_array('array_merge', $globals);
}
protected function initExtensions()

View file

@ -186,10 +186,21 @@ class Twig_Error extends Exception
protected function guessTemplateInfo()
{
$template = null;
foreach (debug_backtrace() as $trace) {
$templateClass = null;
if (version_compare(phpversion(), '5.3.6', '>=')) {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
} else {
$backtrace = debug_backtrace();
}
foreach ($backtrace as $trace) {
if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) {
if (null === $this->filename || $this->filename == $trace['object']->getTemplateName()) {
$currentClass = get_class($trace['object']);
$isEmbedContainer = 0 === strpos($templateClass, $currentClass);
if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
$template = $trace['object'];
$templateClass = get_class($trace['object']);
}
}
}

View file

@ -365,7 +365,7 @@ class Twig_ExpressionParser
throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename());
}
if ($node instanceof Twig_Node_Expression_Name && null !== $alias = $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
if (!$arg instanceof Twig_Node_Expression_Constant) {
throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s")', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename());
}

View file

@ -191,7 +191,7 @@ class Twig_Extension_Core extends Twig_Extension
new Twig_SimpleFunction('cycle', 'twig_cycle'),
new Twig_SimpleFunction('random', 'twig_random', array('needs_environment' => true)),
new Twig_SimpleFunction('date', 'twig_date_converter', array('needs_environment' => true)),
new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true)),
new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true, 'is_safe' => array('all'))),
);
}
@ -847,21 +847,62 @@ function twig_in_filter($value, $compare)
*/
function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
{
if ($autoescape && is_object($string) && $string instanceof Twig_Markup) {
if ($autoescape && $string instanceof Twig_Markup) {
return $string;
}
if (!is_string($string) && !(is_object($string) && method_exists($string, '__toString'))) {
return $string;
if (!is_string($string)) {
if (is_object($string) && method_exists($string, '__toString')) {
$string = (string) $string;
} else {
return $string;
}
}
if (null === $charset) {
$charset = $env->getCharset();
}
$string = (string) $string;
switch ($strategy) {
case 'html':
// see http://php.net/htmlspecialchars
// Using a static variable to avoid initializing the array
// each time the function is called. Moving the declaration on the
// top of the function slow downs other escaping strategies.
static $htmlspecialcharsCharsets = array(
'ISO-8859-1' => true, 'ISO8859-1' => true,
'ISO-8859-15' => true, 'ISO8859-15' => true,
'utf-8' => true, 'UTF-8' => true,
'CP866' => true, 'IBM866' => true, '866' => true,
'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
'1251' => true,
'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
'BIG5' => true, '950' => true,
'GB2312' => true, '936' => true,
'BIG5-HKSCS' => true,
'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
'EUC-JP' => true, 'EUCJP' => true,
'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
);
if (isset($htmlspecialcharsCharsets[$charset])) {
return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
}
if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
// cache the lowercase variant for future iterations
$htmlspecialcharsCharsets[$charset] = true;
return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
}
$string = twig_convert_encoding($string, 'UTF-8', $charset);
$string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
return twig_convert_encoding($string, $charset, 'UTF-8');
case 'js':
// escape all non-alphanumeric characters
// into their \xHH or \uHHHH representations
@ -915,40 +956,10 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
return $string;
case 'html':
// see http://php.net/htmlspecialchars
// Using a static variable to avoid initializing the array
// each time the function is called. Moving the declaration on the
// top of the function slow downs other escaping strategies.
static $htmlspecialcharsCharsets = array(
'iso-8859-1' => true, 'iso8859-1' => true,
'iso-8859-15' => true, 'iso8859-15' => true,
'utf-8' => true,
'cp866' => true, 'ibm866' => true, '866' => true,
'cp1251' => true, 'windows-1251' => true, 'win-1251' => true,
'1251' => true,
'cp1252' => true, 'windows-1252' => true, '1252' => true,
'koi8-r' => true, 'koi8-ru' => true, 'koi8r' => true,
'big5' => true, '950' => true,
'gb2312' => true, '936' => true,
'big5-hkscs' => true,
'shift_jis' => true, 'sjis' => true, '932' => true,
'euc-jp' => true, 'eucjp' => true,
'iso8859-5' => true, 'iso-8859-5' => true, 'macroman' => true,
);
if (isset($htmlspecialcharsCharsets[strtolower($charset)])) {
return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
}
$string = twig_convert_encoding($string, 'UTF-8', $charset);
$string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
return twig_convert_encoding($string, $charset, 'UTF-8');
case 'url':
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
// hackish test to avoid version_compare that is much slower, this works unless PHP releases a 5.10.*
// at that point however PHP 5.2.* support can be removed
if (PHP_VERSION < '5.3.0') {
return str_replace('%7E', '~', rawurlencode($string));
}
@ -1262,11 +1273,11 @@ function twig_test_iterable($value)
/**
* Renders a template.
*
* @param string template The template to render
* @param array variables The variables to pass to the template
* @param Boolean with_context Whether to pass the current context variables or not
* @param Boolean ignore_missing Whether to ignore missing templates or not
* @param Boolean sandboxed Whether to sandbox the template or not
* @param string $template The template to render
* @param array $variables The variables to pass to the template
* @param Boolean $with_context Whether to pass the current context variables or not
* @param Boolean $ignore_missing Whether to ignore missing templates or not
* @param Boolean $sandboxed Whether to sandbox the template or not
*
* @return string The rendered template
*/
@ -1284,7 +1295,7 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a
}
try {
return $env->resolveTemplate($template)->display($variables);
return $env->resolveTemplate($template)->render($variables);
} catch (Twig_Error_Loader $e) {
if (!$ignoreMissing) {
throw $e;

View file

@ -24,6 +24,7 @@ class Twig_Extension_Debug extends Twig_Extension
// false means that it was not set (and the default is on) or it explicitly enabled
// xdebug.overload_var_dump produces HTML only when html_errors is also enabled
&& (false === ini_get('html_errors') || ini_get('html_errors'))
|| 'cli' === php_sapi_name()
;
return array(

View file

@ -33,7 +33,7 @@ class Twig_Extension_StringLoader extends Twig_Extension
* Loads a template from a string.
*
* <pre>
* {% include template_from_string("Hello {{ name }}") }}
* {{ include(template_from_string("Hello {{ name }}")) }}
* </pre>
*
* @param Twig_Environment $env A Twig_Environment instance

View file

@ -62,8 +62,6 @@ abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableI
if (isset($this->options['is_safe_callback'])) {
return call_user_func($this->options['is_safe_callback'], $filterArgs);
}
return null;
}
public function getPreservesSafety()

View file

@ -76,8 +76,12 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf
}
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && $loader->exists($name)) {
return $this->hasSourceCache[$name] = true;
if ($loader instanceof Twig_ExistsLoaderInterface) {
if ($loader->exists($name)) {
return $this->hasSourceCache[$name] = true;
}
continue;
}
try {

View file

@ -16,6 +16,9 @@
*/
class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
/** Identifier of the main namespace. */
const MAIN_NAMESPACE = '__main__';
protected $paths;
protected $cache;
@ -24,9 +27,11 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
*
* @param string|array $paths A path or an array of paths where to look for templates
*/
public function __construct($paths)
public function __construct($paths = array())
{
$this->setPaths($paths);
if ($paths) {
$this->setPaths($paths);
}
}
/**
@ -36,7 +41,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
*
* @return array The array of paths where to look for templates
*/
public function getPaths($namespace = '__main__')
public function getPaths($namespace = self::MAIN_NAMESPACE)
{
return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array();
}
@ -44,7 +49,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
/**
* Returns the path namespaces.
*
* The "__main__" namespace is always defined.
* The main namespace is always defined.
*
* @return array The array of defined namespaces
*/
@ -59,7 +64,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
* @param string|array $paths A path or an array of paths where to look for templates
* @param string $namespace A path namespace
*/
public function setPaths($paths, $namespace = '__main__')
public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
{
if (!is_array($paths)) {
$paths = array($paths);
@ -79,7 +84,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
*
* @throws Twig_Error_Loader
*/
public function addPath($path, $namespace = '__main__')
public function addPath($path, $namespace = self::MAIN_NAMESPACE)
{
// invalidate the cache
$this->cache = array();
@ -99,7 +104,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
*
* @throws Twig_Error_Loader
*/
public function prependPath($path, $namespace = '__main__')
public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
{
// invalidate the cache
$this->cache = array();
@ -173,7 +178,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
$this->validateName($name);
$namespace = '__main__';
$namespace = self::MAIN_NAMESPACE;
if (isset($name[0]) && '@' == $name[0]) {
if (false === $pos = strpos($name, '/')) {
throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));

View file

@ -98,7 +98,10 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
if (!is_int($name)) {
$named = true;
$name = $this->normalizeName($name);
} elseif ($named) {
throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name')));
}
$parameters[$name] = $node;
}
@ -142,6 +145,10 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
$name = $this->normalizeName($param->name);
if (array_key_exists($name, $parameters)) {
if (array_key_exists($pos, $parameters)) {
throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
}
$arguments[] = $parameters[$name];
unset($parameters[$name]);
} elseif (array_key_exists($pos, $parameters)) {
@ -157,8 +164,8 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
}
}
foreach (array_keys($parameters) as $name) {
throw new Twig_Error_Syntax(sprintf('Unknown argument "%s" for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
if (!empty($parameters)) {
throw new Twig_Error_Syntax(sprintf('Unknown argument%s "%s" for %s "%s".', count($parameters) > 1 ? 's' : '' , implode('", "', array_keys($parameters)), $this->getAttribute('type'), $this->getAttribute('name')));
}
return $arguments;

View file

@ -28,6 +28,17 @@ class Twig_Node_Expression_Test_Constant extends Twig_Node_Expression_Test
->raw('(')
->subcompile($this->getNode('node'))
->raw(' === constant(')
;
if ($this->getNode('arguments')->hasNode(1)) {
$compiler
->raw('get_class(')
->subcompile($this->getNode('arguments')->getNode(1))
->raw(')."::".')
;
}
$compiler
->subcompile($this->getNode('arguments')->getNode(0))
->raw('))')
;

View file

@ -34,7 +34,7 @@ class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test
$this->changeIgnoreStrictCheck($node);
} else {
throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine(), $compiler->getFilename());
throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine());
}
}

View file

@ -107,6 +107,6 @@ class Twig_Node_For extends Twig_Node
$compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\'], $context[\'loop\']);'."\n");
// keep the values set in the inner context for variables defined in the outer context
$compiler->write("\$context = array_merge(\$_parent, array_intersect_key(\$context, \$_parent));\n");
$compiler->write("\$context = array_intersect_key(\$context, \$_parent) + \$_parent;\n");
}
}

View file

@ -20,8 +20,6 @@ class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
}
}
}
return null;
}
protected function setSafe(Twig_NodeInterface $node, array $safe)

View file

@ -80,8 +80,6 @@ class Twig_SimpleFilter
if (null !== $this->options['is_safe_callback']) {
return call_user_func($this->options['is_safe_callback'], $filterArgs);
}
return null;
}
public function getPreservesSafety()

View file

@ -336,21 +336,21 @@ abstract class Twig_Template implements Twig_TemplateInterface
*/
protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
{
$item = ctype_digit((string) $item) ? (int) $item : (string) $item;
// array
if (Twig_TemplateInterface::METHOD_CALL !== $type) {
if ((is_array($object) && array_key_exists($item, $object))
|| ($object instanceof ArrayAccess && isset($object[$item]))
$arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
if ((is_array($object) && array_key_exists($arrayItem, $object))
|| ($object instanceof ArrayAccess && isset($object[$arrayItem]))
) {
if ($isDefinedTest) {
return true;
}
return $object[$item];
return $object[$arrayItem];
}
if (Twig_TemplateInterface::ARRAY_CALL === $type) {
if (Twig_TemplateInterface::ARRAY_CALL === $type || !is_object($object)) {
if ($isDefinedTest) {
return false;
}
@ -360,11 +360,13 @@ abstract class Twig_Template implements Twig_TemplateInterface
}
if (is_object($object)) {
throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $arrayItem, get_class($object)), -1, $this->getTemplateName());
} elseif (is_array($object)) {
throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $item, implode(', ', array_keys($object))), -1, $this->getTemplateName());
throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))), -1, $this->getTemplateName());
} elseif (Twig_TemplateInterface::ARRAY_CALL === $type) {
throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
} else {
throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a "%s" variable', $item, gettype($object)), -1, $this->getTemplateName());
throw new Twig_Error_Runtime(sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
}
}
}
@ -378,14 +380,14 @@ abstract class Twig_Template implements Twig_TemplateInterface
return null;
}
throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist', $item, is_array($object) ? 'Array' : $object), -1, $this->getTemplateName());
throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
}
$class = get_class($object);
// object property
if (Twig_TemplateInterface::METHOD_CALL !== $type) {
if (isset($object->$item) || array_key_exists($item, $object)) {
if (isset($object->$item) || array_key_exists((string) $item, $object)) {
if ($isDefinedTest) {
return true;
}
@ -405,13 +407,13 @@ abstract class Twig_Template implements Twig_TemplateInterface
$lcItem = strtolower($item);
if (isset(self::$cache[$class]['methods'][$lcItem])) {
$method = $item;
$method = (string) $item;
} elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
$method = 'get'.$item;
} elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
$method = 'is'.$item;
} elseif (isset(self::$cache[$class]['methods']['__call'])) {
$method = $item;
$method = (string) $item;
} else {
if ($isDefinedTest) {
return false;

View file

@ -38,8 +38,6 @@ class Twig_TokenParser_Extends extends Twig_TokenParser
$this->parser->setParent($this->parser->getExpressionParser()->parseExpression());
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
return null;
}
/**

View file

@ -49,8 +49,6 @@ class Twig_TokenParser_Macro extends Twig_TokenParser
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$this->parser->setMacro($name, new Twig_Node_Macro($name, new Twig_Node_Body(array($body)), $arguments, $lineno, $this->getTag()));
return null;
}
public function decideBlockEnd(Twig_Token $token)

View file

@ -68,8 +68,6 @@ class Twig_TokenParser_Use extends Twig_TokenParser
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$this->parser->addTrait(new Twig_Node(array('template' => $template, 'targets' => new Twig_Node($targets))));
return null;
}
/**

View file

@ -111,8 +111,6 @@ class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface
}
$broker = prev($this->brokers);
}
return null;
}
public function getParsers()