Библиотека для cis, online, cms1

This commit is contained in:
Фёдор Подлеснов 2016-06-29 18:51:32 +03:00
commit 3c2e614d87
269 changed files with 39854 additions and 0 deletions

788
PHPTAL.php Normal file
View file

@ -0,0 +1,788 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
define('PHPTAL_VERSION', '1_1_14');
//{{{PHPTAL_DIR
if (!defined('PHPTAL_DIR')) define('PHPTAL_DIR',dirname(__FILE__).DIRECTORY_SEPARATOR);
else assert('substr(PHPTAL_DIR,-1) == DIRECTORY_SEPARATOR');
//}}}
/* Please don't use the following constants. They have been replaced by methods in the PHPTAL class and are kept for backwards compatibility only. */
//{{{
if (!defined('PHPTAL_PHP_CODE_DESTINATION')) {
if (function_exists('sys_get_temp_dir')) define('PHPTAL_PHP_CODE_DESTINATION',rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR);
else if (substr(PHP_OS,0,3) == 'WIN') {
if (file_exists('c:\\WINNT\\Temp\\')) define('PHPTAL_PHP_CODE_DESTINATION', 'c:\\WINNT\\Temp\\');
else define('PHPTAL_PHP_CODE_DESTINATION', 'c:\\WINDOWS\\Temp\\');
}
else define('PHPTAL_PHP_CODE_DESTINATION', '/tmp/');
}
if (!defined('PHPTAL_DEFAULT_ENCODING')) define('PHPTAL_DEFAULT_ENCODING', 'UTF-8');
if (!defined('PHPTAL_PHP_CODE_EXTENSION')) define('PHPTAL_PHP_CODE_EXTENSION', 'php');
//}}}
define('PHPTAL_XHTML', 1);
define('PHPTAL_XML', 2);
require_once PHPTAL_DIR.'PHPTAL/FileSource.php';
require_once PHPTAL_DIR.'PHPTAL/RepeatController.php';
require_once PHPTAL_DIR.'PHPTAL/Context.php';
require_once PHPTAL_DIR.'PHPTAL/Exception.php';
require_once PHPTAL_DIR.'PHPTAL/TalesRegistry.php';
/**
* PHPTAL template entry point.
*
* <code>
* <?php
* require_once 'PHPTAL.php';
* try {
* $tpl = new PHPTAL('mytemplate.html');
* $tpl->title = 'Welcome here';
* $tpl->result = range(1, 100);
* ...
* echo $tpl->execute();
* }
* catch (Exception $e) {
* echo $e;
* }
* ?>
* </code>
*
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL
{
const XHTML = 1;
const XML = 2;
/**
* PHPTAL Constructor.
*
* @param string $path Template file path.
*/
public function __construct($path=false)
{
$this->_path = $path;
$this->_repositories = array();
if (defined('PHPTAL_TEMPLATE_REPOSITORY')){
$this->_repositories[] = PHPTAL_TEMPLATE_REPOSITORY;
}
$this->_resolvers = array();
$this->_globalContext = new StdClass();
$this->_context = new PHPTAL_Context();
$this->_context->setGlobal($this->_globalContext);
}
/**
* create
* returns a new PHPTAL object
*
* @param string $path Template file path.
* @return PHPTAL
*/
public static function create($path=false)
{
return new PHPTAL($path);
}
/**
* Clone template state and context.
*/
public function __clone()
{
$context = $this->_context;
$this->_context = clone $this->_context;
$this->_context->setParent($context);
$this->_context->setGlobal($this->_globalContext);
}
/**
* Set template from file path.
* @param $path string
*/
public function setTemplate($path)
{
$this->_prepared = false;
$this->_functionName = null;
$this->_path = $path;
$this->_source = null;
return $this;
}
/**
* Set template from source.
*
* Should be used only with temporary template sources. Use setTemplate() whenever possible.
*
* @param $src string The phptal template source.
* @param path string Fake and 'unique' template path.
*/
public function setSource($src, $path=false)
{
if ($path == false)
$path = '<string> '.md5($src);
require_once PHPTAL_DIR.'PHPTAL/StringSource.php';
$this->_source = new PHPTAL_StringSource($src, $path);
$this->_path = $path;
return $this;
}
/**
* Specify where to look for templates.
*
* @param $rep mixed string or Array of repositories
*/
public function setTemplateRepository($rep)
{
if (is_array($rep)){
$this->_repositories = $rep;
}
else {
$this->_repositories[] = $rep;
}
return $this;
}
/**
* Get template repositories.
*/
public function getTemplateRepositories()
{
return $this->_repositories;
}
/**
* Clears the template repositories.
*/
public function clearTemplateRepositories()
{
$this->_repositories = array();
return $this;
}
/**
* Ignore XML/XHTML comments on parsing.
* @param $bool bool
*/
public function stripComments($bool)
{
$this->_stripComments = $bool;
return $this;
}
/**
* Set output mode
* XHTML output mode will force elements like <link/>, <meta/> and <img/>, etc. to be empty
* and threats attributes like selected, checked to be boolean attributes.
*
* XML output mode outputs XML without such modifications and is neccessary to generate RSS feeds properly.
* @param $mode int (PHPTAL::XML or PHPTAL::XHTML).
*/
public function setOutputMode($mode=PHPTAL_XHTML)
{
if ($mode != PHPTAL::XHTML && $mode != PHPTAL::XML){
throw new PHPTAL_Exception('Unsupported output mode '.$mode);
}
$this->_outputMode = $mode;
return $this;
}
/**
* Get output mode
*/
public function getOutputMode()
{
return $this->_outputMode;
}
/**
* Set input and ouput encoding.
* @param $enc string example: 'UTF-8'
*/
public function setEncoding($enc)
{
$this->_encoding = $enc;
if ($this->_translator) $this->_translator->setEncoding($enc);
return $this;
}
/**
* Get input and ouput encoding.
* @param $enc string example: 'UTF-8'
*/
public function getEncoding()
{
return $this->_encoding;
}
/**
* Set the storage location for intermediate PHP files. The path cannot contain characters that would be interpreted by glob() (e.g. * or ?)
* @param string $path Intermediate file path.
*/
public function setPhpCodeDestination($path)
{
$this->_phpCodeDestination = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
return $this;
}
/**
* Get the storage location for intermediate PHP files.
*/
public function getPhpCodeDestination()
{
return $this->_phpCodeDestination;
}
/**
* Set the file extension for intermediate PHP files.
* @param string $extension The file extension.
*/
public function setPhpCodeExtension($extension)
{
$this->_phpCodeExtension = $extension;
return $this;
}
/**
* Get the file extension for intermediate PHP files.
*/
public function getPhpCodeExtension()
{
return $this->_phpCodeExtension;
}
/**
* Flags whether to ignore intermediate php files and to
* reparse templates every time (if set to true).
* Don't use in production - this makes PHPTAL significantly slower.
* @param bool bool Forced reparse state.
*/
public function setForceReparse($bool)
{
$this->_forceReparse = (bool) $bool;
return $this;
}
/**
* Get the value of the force reparse state.
*/
public function getForceReparse()
{
return $this->_forceReparse !== NULL ? $this->_forceReparse : (defined('PHPTAL_FORCE_REPARSE') && PHPTAL_FORCE_REPARSE);
}
/**
* Set I18N translator.
* This sets encoding used by the translator, so be sure to use encoding-dependent features of the translator (e.g. addDomain) _after_ calling setTranslator.
*/
public function setTranslator(PHPTAL_TranslationService $t)
{
$this->_translator = $t;
$this->_translator->setEncoding($this->getEncoding());
return $this;
}
/**
* Set template pre filter. It will be called once before template is compiled.
*/
public function setPreFilter(PHPTAL_Filter $filter)
{
$this->_prefilter = $filter;
return $this;
}
/**
* Set template post filter. It will be called every time after template generates output.
*/
public function setPostFilter(PHPTAL_Filter $filter)
{
$this->_postfilter = $filter;
return $this;
}
/**
* Register a trigger for specified phptal:id.
* @param $id string phptal:id to look for
*/
public function addTrigger($id, PHPTAL_Trigger $trigger)
{
$this->_triggers[$id] = $trigger;
return $this;
}
/**
* Returns trigger for specified phptal:id.
* @param $id string phptal:id
*/
public function getTrigger($id)
{
if (array_key_exists($id, $this->_triggers)){
return $this->_triggers[$id];
}
return null;
}
/**
* Set a context variable.
* @param $varname string
* @param $value mixed
*/
public function __set($varname, $value)
{
$this->_context->__set($varname, $value);
}
/**
* Set a context variable.
* @param $varname string
* @param $value mixed
*/
public function set($varname, $value)
{
$this->_context->__set($varname, $value);
return $this;
}
/**
* Execute the template code.
*
* @return string
*/
public function execute()
{
if (!$this->_prepared) {
$this->prepare();
}
// includes generated template PHP code
$this->_context->__file = $this->__file;
require_once $this->getCodePath();
$templateFunction = $this->getFunctionName();
try {
ob_start();
$templateFunction($this, $this->_context);
$res = ob_get_clean();
}
catch (Exception $e){
ob_end_clean();
throw $e;
}
// unshift doctype
$docType = $this->_context->__docType;
if ($docType){
$res = $docType . "\n" . $res;
}
// unshift xml declaration
$xmlDec = $this->_context->__xmlDeclaration;
if ($xmlDec){
$res = $xmlDec . "\n" . $res;
}
if ($this->_postfilter != null){
return $this->_postfilter->filter($res);
}
return $res;
}
protected function setConfigurationFrom(PHPTAL $from)
{
// use references - this way config of both objects will be more-or-less in sync
$this->_encoding = &$from->_encoding;
$this->_outputMode = &$from->_outputMode;
$this->_stripComments = &$from->_stripComments;
$this->_forceReparse = &$from->_forceReparse;
$this->_phpCodeDestination = &$from->_phpCodeDestination;
$this->_phpCodeExtension = &$from->_phpCodeExtension;
$this->_cacheLifetime = &$from->_cacheLifetime;
$this->_cachePurgeFrequency = &$from->_cachePurgeFrequency;
$this->setTemplateRepository($from->_repositories);
array_unshift($this->_repositories, dirname($from->_source->getRealPath()));
$this->_resolvers = &$from->_resolvers;
$this->_prefilter = &$from->_prefilter;
$this->_postfilter = &$from->_postfilter;
}
private $externalMacroTempaltesCache = array();
/**
* Execute a template macro.
* Should be used only from within generated template code!
*
* @param $path string Template macro path
*/
public function executeMacro($path)
{
// extract macro source file from macro name, if not source file
// found in $path, then the macro is assumed to be local
if (preg_match('/^(.*?)\/([a-z0-9_]*)$/i', $path, $m)){
list(,$file,$macroName) = $m;
if (isset($this->externalMacroTempaltesCache[$file]))
{
$tpl = $this->externalMacroTempaltesCache[$file];
}
else
{
$tpl = new PHPTAL($file);
$tpl->setConfigurationFrom($this);
$tpl->prepare();
// require PHP generated code
require_once $tpl->getCodePath();
$this->externalMacroTempaltesCache[$file] = $tpl;
if (count($this->externalMacroTempaltesCache) > 10) $this->externalMacroTempaltesCache = array(); // keep it small (typically only 1 or 2 external files are used)
}
// save current file
$currentFile = $this->_context->__file;
$this->_context->__file = $tpl->__file;
$fun = $tpl->getFunctionName() . '_' . $macroName;
if (!function_exists($fun)) throw new PHPTAL_Exception("Macro '$macroName' is not defined in $file",$this->_source->getRealPath());
$fun($this, $this->_context);
// restore current file
$this->_context->__file = $currentFile;
}
else
{
// call local macro
$fun = $this->getFunctionName() . '_' . trim($path);
if (!function_exists($fun)) throw new PHPTAL_Exception("Macro '$path' is not defined",$this->_source->getRealPath());
$fun( $this, $this->_context );
}
}
private function setCodeFile()
{
$this->_codeFile = $this->getPhpCodeDestination() . $this->getFunctionName() . '.' . $this->getPhpCodeExtension();
}
/**
* Prepare template without executing it.
*/
public function prepare()
{
// clear just in case settings changed and cache is out of date
$this->externalMacroTempaltesCache = array();
// find the template source file
$this->findTemplate();
$this->__file = $this->_source->getRealPath();
$this->setCodeFile();
// parse template if php generated code does not exists or template
// source file modified since last generation of PHPTAL_FORCE_REPARSE
// is defined.
if ($this->getForceReparse() || !file_exists($this->_codeFile))
{
if ($this->getCachePurgeFrequency() && mt_rand()%$this->getCachePurgeFrequency() == 0)
{
$this->cleanUpGarbage();
}
$this->parse();
}
$this->_prepared = true;
return $this;
}
public function getCacheLifetime()
{
return $this->_cacheLifetime;
}
/**
* how long compiled templates and phptal:cache files are kept, in days
*/
public function setCacheLifetime($days)
{
$this->_cacheLifetime = max(0.5,$days);
return $this;
}
/**
* PHPTAL will scan cache and remove old files on every nth compile
* Set to 0 to disable cleanups
*/
public function setCachePurgeFrequency($n)
{
$this->_cachePurgeFrequency = (int)$n;
return $this;
}
public function getCachePurgeFrequency()
{
return $this->_cachePurgeFrequency;
}
/**
* Removes all compiled templates from cache after PHPTAL_CACHE_LIFETIME days
*/
public function cleanUpGarbage()
{
$phptalCacheFilesExpire = time() - $this->getCacheLifetime() * 3600 * 24;
$upperLimit = $this->getPhpCodeDestination() . 'tpl_' . $phptalCacheFilesExpire . '_';
$lowerLimit = $this->getPhpCodeDestination() . 'tpl_0_';
$phptalCacheFiles = glob($this->getPhpCodeDestination() . 'tpl_*.' . $this->getPhpCodeExtension() . '*');
if ($phptalCacheFiles)
{
foreach($phptalCacheFiles as $index => $file)
{
if ($file > $upperLimit && substr($file,0,strlen($lowerLimit)) !== $lowerLimit)
{
unset($phptalCacheFiles[$index]);
}
}
foreach($phptalCacheFiles as $file)
{
$time = filemtime($file);
if ($time && $time < $phptalCacheFilesExpire) @unlink($file);
}
}
}
/**
* Removes single compiled template from cache and all its fragments cached by phptal:cache.
* Must be called after setSource/setTemplate.
*/
public function cleanUpCache()
{
if (!$this->getCodePath())
{
$this->findTemplate(); $this->setCodeFile();
if (!$this->getCodePath()) throw new PHPTAL_Exception("No codefile");
}
$filename = $this->getCodePath();
$phptalCacheFiles = glob($filename . '*');
if ($phptalCacheFiles) foreach($phptalCacheFiles as $file)
{
if (substr($file, 0, strlen($filename)) !== $filename) continue; // safety net
@unlink($file);
}
$this->_prepared = false;
}
/**
* Returns the path of the intermediate PHP code file.
*
* The returned file may be used to cleanup (unlink) temporary files
* generated by temporary templates or more simply for debug.
*
* @return string
*/
public function getCodePath()
{
return $this->_codeFile;
}
/**
* Returns the generated template function name.
* @return string
*/
public function getFunctionName()
{
if (!$this->_functionName) {
$this->_functionName = 'tpl_' . $this->_source->getLastModifiedTime() . '_' . PHPTAL_VERSION .
substr(preg_replace('/[^a-zA-Z]/','',basename($this->_source->getRealPath())),0,10) . md5($this->_source->getRealPath());
}
return $this->_functionName;
}
/**
* Returns template translator.
* @return PHPTAL_TranslationService
*/
public function getTranslator()
{
return $this->_translator;
}
/**
* Returns array of exceptions caught by tal:on-error attribute.
* @return array<Exception>
*/
public function getErrors()
{
return $this->_errors;
}
/**
* Public for phptal templates, private for user.
* @access private
*/
public function addError(Exception $error)
{
array_push($this->_errors, $error);
return $this;
}
/**
* Returns current context object.
* Use only in Triggers.
*
* @return PHPTAL_Context
*/
public function getContext()
{
return $this->_context;
}
/**
* only for use in generated template code
* @access private
*/
public function getGlobalContext()
{
return $this->_globalContext;
}
/**
* only for use in generated template code
* @access private
*/
public function pushContext()
{
$this->_context = $this->_context->pushContext();
return $this->_context;
}
/**
* only for use in generated template code
* @access private
*/
public function popContext()
{
$this->_context = $this->_context->popContext();
return $this->_context;
}
protected function parse()
{
require_once PHPTAL_DIR.'PHPTAL/Dom/Parser.php';
// instantiate the PHPTAL source parser
$parser = new PHPTAL_Dom_Parser($this->_encoding);
$parser->stripComments($this->_stripComments);
$data = $this->_source->getData();
$realpath = $this->_source->getRealPath();
if ($this->_prefilter)
$data = $this->_prefilter->filter($data);
$tree = $parser->parseString($data, $realpath);
require_once PHPTAL_DIR.'PHPTAL/Php/CodeGenerator.php';
$generator = new PHPTAL_Php_CodeGenerator($this->getFunctionName(), $this->_source->getRealPath());
$generator->setEncoding($this->_encoding);
$generator->setOutputMode($this->_outputMode);
$generator->generate($tree);
if (!@file_put_contents($this->_codeFile, $generator->getResult())) {
throw new PHPTAL_Exception('Unable to open '.$this->_codeFile.' for writing');
}
return $this;
}
/**
* Search template source location.
*/
protected function findTemplate()
{
if ($this->_path == false){
throw new PHPTAL_Exception('No template file specified');
}
// template source already defined
if ($this->_source != null){
return;
}
array_push($this->_resolvers, new PHPTAL_FileSourceResolver($this->_repositories));
foreach ($this->_resolvers as $resolver){
$source = $resolver->resolve($this->_path);
if ($source != null){
$this->_source = $source;
break;
}
}
array_pop($this->_resolvers);
if ($this->_source == null){
throw new PHPTAL_Exception('Unable to locate template file '.$this->_path);
}
}
protected $_prefilter = null;
protected $_postfilter = null;
// list of template source repositories
protected $_repositories = array();
// template path
protected $_path = null;
// template source resolvers
protected $_resolvers = array();
// template source (only set when not working with file)
protected $_source = null;
// destination of PHP intermediate file
protected $_codeFile = null;
// php function generated for the template
protected $_functionName = null;
// set to true when template is ready for execution
protected $_prepared = false;
// associative array of phptal:id => PHPTAL_Trigger
protected $_triggers = array();
// i18n translator
protected $_translator = null;
// global execution context
protected $_globalContext = null;
// current execution context
protected $_context = null;
// current template file (changes within macros)
public $__file = false;
// list of on-error caught exceptions
protected $_errors = array();
protected $_encoding = PHPTAL_DEFAULT_ENCODING;
protected $_outputMode = PHPTAL::XHTML;
protected $_stripComments = false;
// configuration properties
protected $_forceReparse = NULL;
protected $_phpCodeDestination = PHPTAL_PHP_CODE_DESTINATION;
protected $_phpCodeExtension = PHPTAL_PHP_CODE_EXTENSION;
protected $_cacheLifetime = 30;
protected $_cachePurgeFrequency = 50;
}
?>

12
PHPTAL/CommentFilter.php Normal file
View file

@ -0,0 +1,12 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Filter.php';
class PHPTAL_CommentFilter implements PHPTAL_Filter
{
public function filter($src){
return preg_replace('/(<!--.*?-->)/s', '', $src);
}
}
?>

405
PHPTAL/Context.php Normal file
View file

@ -0,0 +1,405 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* This class handles template execution context.
* @package phptal
*/
class PHPTAL_Context
{
public static $USE_GLOBAL = false;
public $__line = false;
public $__file = false;
public $__repeat;
public $__xmlDeclaration;
public $__docType;
public $__nothrow;
public $__translator;
public function __construct()
{
$this->__repeat = new StdClass();
}
public function __clone()
{
$this->__repeat = clone($this->__repeat);
}
public function setParent(PHPTAL_Context $parent)
{
$this->_parentContext = $parent;
}
public function setGlobal(StdClass $globalContext)
{
$this->_globalContext = $globalContext;
}
public function pushContext()
{
if (self::$USE_GLOBAL) return $this;
$res = clone $this;
$res->setParent($this);
return $res;
}
public function popContext()
{
if (self::$USE_GLOBAL) return $this;
return $this->_parentContext;
}
/**
* Set output document type if not already set.
*
* This method ensure PHPTAL uses the first DOCTYPE encountered (main
* template or any macro template source containing a DOCTYPE.
*/
public function setDocType($doctype)
{
if ($this->_parentContext != null){
return $this->_parentContext->setDocType($doctype);
}
if ($this->_parentContext != null){
return $this->_parentContext->setDocType($doctype);
}
if (!$this->__docType){
$this->__docType = $doctype;
}
}
/**
* Set output document xml declaration.
*
* This method ensure PHPTAL uses the first xml declaration encountered
* (main template or any macro template source containing an xml
* declaration).
*/
public function setXmlDeclaration($xmldec)
{
if ($this->_parentContext != null){
return $this->_parentContext->setXmlDeclaration($xmldec);
}
if ($this->_parentContext != null){
return $this->_parentContext->setXmlDeclaration($xmldec);
}
if (!$this->__xmlDeclaration){
$this->__xmlDeclaration = $xmldec;
}
}
/**
* Activate or deactivate exception throwing during unknown path
* resolution.
*/
public function noThrow($bool)
{
$this->__nothrow = $bool;
}
/**
* Returns true if specified slot is filled.
*/
public function hasSlot($key)
{
if ($this->_parentContext) return $this->_parentContext->hasSlot($key); // setting slots in any context
return array_key_exists($key, $this->_slots);
}
/**
* Returns the content of specified filled slot.
*/
public function getSlot($key)
{
if ($this->_parentContext) return $this->_parentContext->getSlot($key); // setting slots in any context
return $this->_slots[$key];
}
/**
* Fill a macro slot.
*/
public function fillSlot($key, $content)
{
if ($this->_parentContext) $this->_parentContext->fillSlot($key,$content); // setting slots in any context
else $this->_slots[$key] = $content;
}
/**
* Push current filled slots on stack.
*/
public function pushSlots()
{
array_push($this->_slotsStack, $this->_slots);
$this->_slots = array();
}
/**
* Restore filled slots stack.
*/
public function popSlots()
{
$this->_slots = array_pop($this->_slotsStack);
}
/**
* Context setter.
*/
public function __set($varname, $value)
{
if ($varname[0] == '_')
{
throw new PHPTAL_Exception('Template variable error \''.$varname.'\' must not begin with underscore');
}
$this->$varname = $value;
}
/**
* Context getter.
*/
public function __get($varname)
{
if ($varname == 'repeat')
return $this->__repeat;
if (isset($this->$varname)){
return $this->$varname;
}
if (isset($this->_globalContext->$varname)){
return $this->_globalContext->$varname;
}
if (defined($varname))
{
return constant($varname);
}
if ($this->__nothrow)
return null;
$e = sprintf('Unable to find path %s in current scope', $varname);
throw new PHPTAL_Exception($e, $this->__file, $this->__line);
}
private $_slots = array();
private $_slotsStack = array();
private $_parentContext = null;
private $_globalContext = null;
}
// emulate property_exists() function, this is slow but much better than
// isset(), use next release of PHP5 as soon as available !
if (!function_exists('property_exists')){
function property_exists($o, $property)
{
return array_key_exists($property, get_object_vars($o));
}
}
/**
* Resolve TALES path starting from the first path element.
*
* The TALES path : object/method1/10/method2
* will call : phptal_path($ctx->object, 'method1/10/method2')
*
* The nothrow param is used by phptal_exists() and prevent this function to
* throw an exception when a part of the path cannot be resolved, null is
* returned instead.
*/
function phptal_path($base, $path, $nothrow=false)
{//{{{
$parts = explode('/', $path);
$current = true;
if ($base === null)
{
if ($nothrow) return null;
throw new PHPTAL_Exception("Trying to read property '$path' from NULL");
}
while (($current = array_shift($parts)) !== null){
// object handling
if (is_object($base)){
// look for method
if (method_exists($base, $current)){
$base = $base->$current();
continue;
}
// look for variable
if (property_exists($base, $current)){
$base = $base->$current;
continue;
}
if ($base instanceof ArrayAccess && $base->offsetExists($current))
{
$base = $base->offsetGet($current);
continue;
}
if ($base instanceof Countable && ($current === 'length' || $current === 'size'))
{
$base = count($base);
continue;
}
// look for isset (priority over __get)
if (method_exists($base,'__isset') && is_callable(array($base, '__isset')))
{
if ($base->__isset($current)){
$base = $base->$current;
continue;
}
}
// ask __get and discard if it returns null
else if (method_exists($base,'__get') && is_callable(array($base, '__get')))
{
$tmp = $base->$current;
if (NULL !== $tmp){
$base = $tmp;
continue;
}
}
// magic method call
if (method_exists($base, '__call')){
try
{
$base = $base->$current();
continue;
}
catch(BadMethodCallException $e){}
}
// emulate array behaviour
if (is_numeric($current) && method_exists($base, '__getAt')){
$base = $base->__getAt($current);
continue;
}
if ($nothrow)
return null;
$err = 'Unable to find part "%s" in path "%s" inside '.(is_object($base)?get_class($base):gettype($base));
$err = sprintf($err, $current, $path);
throw new PHPTAL_Exception($err);
}
// array handling
if (is_array($base)) {
// key or index
if (array_key_exists((string)$current, $base)){
$base = $base[$current];
continue;
}
// virtual methods provided by phptal
if ($current == 'length' || $current == 'size'){
$base = count($base);
continue;
}
if ($nothrow)
return null;
$err = 'Unable to find array key "%s" in path "%s"';
$err = sprintf($err, $current, $path);
throw new PHPTAL_Exception($err);
}
// string handling
if (is_string($base)) {
// virtual methods provided by phptal
if ($current == 'length' || $current == 'size'){
$base = strlen($base);
continue;
}
// access char at index
if (is_numeric($current)){
$base = $base[$current];
continue;
}
}
// if this point is reached, then the part cannot be resolved
if ($nothrow)
return null;
$err = 'Unable to find part "%s" in path "%s" with base "%s"';
$err = sprintf($err, $current, $path, is_scalar($base)?"$base":(is_object($base)?get_class($base):gettype($base)));
throw new PHPTAL_Exception($err);
}
return $base;
}
function phptal_true($ctx, $path)
{
$ctx->noThrow(true);
$res = phptal_path($ctx, $path, true);
$ctx->noThrow(false);
return !!$res;
}
/**
* Returns true if $path can be fully resolved in $ctx context.
*/
function phptal_exists($ctx, $path)
{
// special note: this method may requires to be extended to a full
// phptal_path() sibling to avoid calling latest path part if it is a
// method or a function...
$ctx->noThrow(true);
$res = phptal_path($ctx, $path, true);
$ctx->noThrow(false);
return $res !== NULL;
}
function phptal_isempty($var)
{
return $var === null || $var === false || $var === ''
|| ((is_array($var) || $var instanceof Countable) && count($var)===0);
}
function phptal_escape($var, $ent, $encoding)
{
if (is_string($var)) {
return htmlspecialchars($var, $ent, $encoding);
}
elseif (is_object($var)) {
if ($var instanceof SimpleXMLElement) return $var->asXML();
return htmlspecialchars($var->__toString(), $ent, $encoding);
}
elseif (is_bool($var)){
return (int)$var;
}
return $var;
}
?>

237
PHPTAL/Dom/Defs.php Normal file
View file

@ -0,0 +1,237 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
// From http://dev.zope.org/Wikis/DevSite/Projects/ZPT/TAL%20Specification%201.4
//
// Order of Operations
//
// When there is only one TAL statement per element, the order in which
// they are executed is simple. Starting with the root element, each
// element's statements are executed, then each of its child elements is
// visited, in order, to do the same.
//
// Any combination of statements may appear on the same elements, except
// that the content and replace statements may not appear together.
//
// When an element has multiple statements, they are executed in this
// order:
//
// * define
// * condition
// * repeat
// * content or replace
// * attributes
// * omit-tag
//
// Since the on-error statement is only invoked when an error occurs, it
// does not appear in the list.
//
// The reasoning behind this ordering goes like this: You often want to set
// up variables for use in other statements, so define comes first. The
// very next thing to do is decide whether this element will be included at
// all, so condition is next; since the condition may depend on variables
// you just set, it comes after define. It is valuable be able to replace
// various parts of an element with different values on each iteration of a
// repeat, so repeat is next. It makes no sense to replace attributes and
// then throw them away, so attributes is last. The remaining statements
// clash, because they each replace or edit the statement element.
//
// If you want to override this ordering, you must do so by enclosing the
// element in another element, possibly div or span, and placing some of
// the statements on this new element.
//
require_once PHPTAL_DIR.'PHPTAL/Namespace.php';
require_once PHPTAL_DIR.'PHPTAL/Namespace/TAL.php';
require_once PHPTAL_DIR.'PHPTAL/Namespace/METAL.php';
require_once PHPTAL_DIR.'PHPTAL/Namespace/I18N.php';
require_once PHPTAL_DIR.'PHPTAL/Namespace/PHPTAL.php';
/**
* PHPTAL constants.
*
* This is a pseudo singleton class, a user may decide to provide
* his own singleton instance which will then be used by PHPTAL.
*
* This behaviour is mainly usefull to remove builtin namespaces
* and provide custom ones.
*
* @package phptal.dom
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Dom_Defs
{
public static function getInstance()
{
if (self::$_instance == null){
self::$_instance = new PHPTAL_Dom_Defs();
}
return self::$_instance;
}
public static function setInstance(PHPTAL_Dom_Defs $instance)
{
self::$_instance = $instance;
}
public function __construct()
{
$this->_dictionary = array();
$this->_namespaces = array();
$this->_xmlns = array();
}
public function isEmptyTag($tagName)
{
return in_array(strtolower($tagName), self::$XHTML_EMPTY_TAGS);
}
public function xmlnsToLocalName($xmlns)
{
return $this->_xmlns[$xmlns];
}
/**
* Returns true if the attribute is an xhtml boolean attribute.
*
* @return bool
*/
public function isBooleanAttribute($att)
{
return in_array($att, self::$XHTML_BOOLEAN_ATTRIBUTES);
}
/**
* Returns true if the attribute is in the phptal dictionnary.
*
* @return bool
*/
public function isPhpTalAttribute($att)
{
return array_key_exists(strtolower($att), $this->_dictionary);
}
/**
* Returns true if the attribute is a valid phptal attribute or an unknown
* attribute.
*
* Examples of valid attributes: tal:content, metal:use-slot
* Examples of invalid attributes: tal:unknown, metal:content
*
* @return bool
*/
public function isValidAttribute($att)
{
if (preg_match('/^(.*):(.*)$/', $att, $m)) {
list (,$ns,$sub) = $m;
if (array_key_exists(strtolower($ns), $this->_namespaces)
&& !$this->isPhpTalAttribute($att)) {
return false;
}
}
return true;
}
/**
* Returns true if the attribute is a phptal handled xml namespace
* declaration.
*
* Examples of handled xmlns: xmlns:tal, xmlns:metal
*
* @return bool
*/
public function isHandledXmlNs($att, $value)
{
$att = strtolower($att);
return substr($att, 0, 6) == 'xmlns:'
&& array_key_exists($value, $this->_xmlns);
}
public function getNamespaceAttribute($attName)
{
return $this->_dictionary[strtolower($attName)];
}
/**
* Register a PHPTAL_Namespace and its attribute into PHPTAL.
*/
public function registerNamespace(PHPTAL_Namespace $ns)
{
$nsname = strtolower($ns->name);
$this->_namespaces[$nsname] = $ns;
$this->_xmlns[$ns->xmlns] = $nsname;
foreach ($ns->getAttributes() as $name => $attribute){
$key = $nsname.':'.strtolower($name);
$this->_dictionary[$key] = $attribute;
}
}
private static $_instance = null;
private $_dictionary;
private $_namespaces;
private $_xmlns;
/**
* This array contains XHTML tags that must be echoed in a &lt;tag/&gt; form
* instead of the &lt;tag&gt;&lt;/tag&gt; form.
*
* In fact, some browsers does not support the later form so PHPTAL
* ensure these tags are correctly echoed.
*/
private static $XHTML_EMPTY_TAGS = array(
'area',
'base',
'basefont',
'br',
'col',
'frame',
'hr',
'img',
'input',
'isindex',
'link',
'meta',
'param',
);
/**
* This array contains XHTML boolean attributes, their value is self
* contained (ie: they are present or not).
*/
private static $XHTML_BOOLEAN_ATTRIBUTES = array(
'checked',
'compact',
'declare',
'defer',
'disabled',
'ismap',
'multiple',
'noresize',
'noshade',
'nowrap',
'readonly',
'selected',
);
}
?>

254
PHPTAL/Dom/Node.php Normal file
View file

@ -0,0 +1,254 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Dom/Defs.php';
require_once PHPTAL_DIR.'PHPTAL/Php/CodeWriter.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* Document node abstract class.
*
* @package phptal.dom
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
abstract class PHPTAL_Dom_Node
{
public function __construct()
{
}
public function setSource($file, $line)
{
$this->_file = $file;
$this->_line = $line;
}
public function getSourceLine()
{
return $this->_line;
}
public function getSourceFile()
{
return $this->_file;
}
private $_file;
private $_line;
}
/**
* Node container.
*
* @package phptal.dom
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Dom_Tree extends PHPTAL_Dom_Node
{
public function __construct()
{
parent::__construct();
$this->_children = array();
}
public function addChild(PHPTAL_Dom_Node $node)
{
array_push($this->_children, $node);
}
public function &getChildren()
{
return $this->_children;
}
public function clearChildren()
{
$this->_children = array();
}
protected $_children;
}
/**
* Document Tag representation.
*
* This is the main class used by PHPTAL because TAL is a Template Attribute
* Language, other Node kinds are (usefull) toys.
*
* @package phptal.dom
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Dom_Element extends PHPTAL_Dom_Tree
{
private $name;
public $attributes = array();
public function __construct($name, $attributes)
{
if (!preg_match('/^[a-z_:][a-z0-9._:\x80-\xff-]*$/i',$name)) throw new PHPTAL_Exception("Invalid element name '$name'");
parent::__construct();
$this->name = $name;
$this->attributes = $attributes;
}
public function setXmlnsState(PHPTAL_Dom_XmlnsState $state)
{
$this->_xmlns = $state;
$this->xmlns = $state;
}
public function getName()
{
return $this->name;
}
public function getXmlnsState()
{
return $this->_xmlns;
}
/** Returns true if the element contains specified PHPTAL attribute. */
public function hasAttribute($name)
{
$ns = $this->getNodePrefix();
foreach ($this->attributes as $key=>$value){
if ($this->_xmlns->unAliasAttribute($key) == $name){
return true;
}
if ($ns && $this->_xmlns->unAliasAttribute("$ns:$key") == $name){
return true;
}
}
return false;
}
/** Returns the value of specified PHPTAL attribute. */
public function getAttribute($name)
{
$ns = $this->getNodePrefix();
foreach ($this->attributes as $key=>$value){
if ($this->_xmlns->unAliasAttribute($key) == $name){
return $value;
}
if ($ns && $this->_xmlns->unAliasAttribute("$ns:$key") == $name){
return $value;
}
}
return false;
}
/**
* Returns true if this element or one of its PHPTAL attributes has some
* content to print (an empty text node child does not count).
*/
public function hasRealContent()
{
if (count($this->_children) == 0)
return false;
if (count($this->_children) == 1){
$child = $this->_children[0];
if ($child instanceOf PHPTAL_Dom_Text && $child->getValue() == ''){
return false;
}
}
return true;
}
private function getNodePrefix()
{
$result = false;
if (preg_match('/^(.*?):block$/', $this->name, $m)){
list(,$result) = $m;
}
return $result;
}
private function hasContent()
{
return count($this->_children) > 0;
}
/**
* XMLNS aliases propagated from parent nodes and defined by this node
* attributes.
*/
protected $_xmlns;
}
/**
* @package phptal.dom
*/
class PHPTAL_Dom_ValueNode extends PHPTAL_Dom_Node
{
public function __construct($data)
{
$this->_value = $data;
}
public function getValue()
{
return $this->_value;
}
private $_value;
}
/**
* Document text data representation.
* @package phptal.dom
*/
class PHPTAL_Dom_Text extends PHPTAL_Dom_ValueNode{}
/**
* Preprocessor, etc... representation.
*
* @package phptal.dom
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Dom_Specific extends PHPTAL_Dom_ValueNode {}
/**
* Comment nodes.
* @package phptal.dom
*/
class PHPTAL_Dom_Comment extends PHPTAL_Dom_ValueNode {}
/**
* Document doctype representation.
*
* @package phptal.dom
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Dom_Doctype extends PHPTAL_Dom_ValueNode {}
/**
* XML declaration node.
*
* @package phptal.dom
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Dom_XmlDeclaration extends PHPTAL_Dom_ValueNode {}
?>

154
PHPTAL/Dom/Parser.php Normal file
View file

@ -0,0 +1,154 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Dom/Defs.php';
require_once PHPTAL_DIR.'PHPTAL/Dom/Node.php';
require_once PHPTAL_DIR.'PHPTAL/Dom/XmlParser.php';
require_once PHPTAL_DIR.'PHPTAL/Dom/XmlnsState.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Tales.php';
/**
* Template parser.
*
* @package phptal.dom
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Dom_Parser extends PHPTAL_XmlParser
{
const ERR_DOCUMENT_END_STACK_NOT_EMPTY = "Not all elements were closed before end of the document (element stack not empty)";
const ERR_UNSUPPORTED_ATTRIBUTE = "Unsupported attribute '%s'";
const ERR_ELEMENT_CLOSE_MISMATCH = "Tag closure mismatch, expected '%s' but was '%s'";
public function __construct($input_encoding = 'UTF-8')
{
parent::__construct($input_encoding);
$this->_xmlns = new PHPTAL_Dom_XmlnsState();
}
public function getXmlnsState()
{
return $this->_xmlns;
}
public function stripComments($b)
{
$this->_stripComments = $b;
}
public function parseString($src, $filename = '<string>')
{
parent::parseString($src, $filename);
return $this->_tree;
}
public function parseFile($path)
{
parent::parseFile($path);
return $this->_tree;
}
// ~~~~~ XmlParser implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public function onDocumentStart()
{
$this->_tree = new PHPTAL_Dom_Tree();
$this->_tree->setSource($this->getSourceFile(), $this->getLineNumber());
$this->_stack = array();
$this->_current = $this->_tree;
}
public function onDocumentEnd()
{
if (count($this->_stack) > 0) {
$this->raiseError(self::ERR_DOCUMENT_END_STACK_NOT_EMPTY);
}
}
public function onDocType($doctype)
{
$this->pushNode(new PHPTAL_Dom_DocType($doctype));
}
public function onXmlDecl($decl)
{
$this->pushNode(new PHPTAL_Dom_XmlDeclaration($decl));
}
public function onComment($data)
{
if ($this->_stripComments)
return;
$this->pushNode(new PHPTAL_Dom_Comment($data));
}
public function onSpecific($data)
{
$this->pushNode(new PHPTAL_Dom_Specific($data));
}
public function onElementStart($name, $attributes)
{
$this->_xmlns = PHPTAL_Dom_XmlnsState::newElement($this->_xmlns, $attributes);
foreach ($attributes as $key=>$value) {
if (!$this->_xmlns->isValidAttribute($key)) {
$this->raiseError(self::ERR_UNSUPPORTED_ATTRIBUTE, $key);
}
}
$node = new PHPTAL_Dom_Element($name, $attributes);
$node->setXmlnsState($this->getXmlnsState());
$this->pushNode($node);
array_push($this->_stack, $this->_current);
$this->_current = $node;
}
public function onElementData($data)
{
$this->pushNode(new PHPTAL_Dom_Text($data));
}
public function onElementClose($name)
{
if (!$this->_current instanceof PHPTAL_Dom_Element) $this->raiseError("Found closing tag for '$name' where there are no open tags");
if ($this->_current->getName() != $name) {
$this->raiseError(self::ERR_ELEMENT_CLOSE_MISMATCH, $this->_current->getName(), $name);
}
$this->_current = array_pop($this->_stack);
if ($this->_current instanceOf PHPTAL_Dom_Element)
$this->_xmlns = $this->_current->getXmlnsState();
}
private function pushNode(PHPTAL_Dom_Node $node)
{
$node->setSource($this->getSourceFile(), $this->getLineNumber());
$this->_current->addChild($node);
}
private $_tree; /* PHPTAL_Dom_Parser_NodeTree */
private $_stack; /* array<PHPTAL_Dom_Parser_Node> */
private $_current; /* PHPTAL_Dom_Parser_Node */
private $_xmlns; /* PHPTAL_Dom_Parser_XmlnsState */
private $_stripComments = false;
}
?>

386
PHPTAL/Dom/XmlParser.php Normal file
View file

@ -0,0 +1,386 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* Simple sax like xml parser for PHPTAL.
*
* Because PHP Xml parser libraries tends to fail giving a real xml document
* representation (at the time this file was created, it was impossible to
* retrieve doctypes, xml declaration, problem with comments and CDATA) this
* parser was created and can be manipulated to accept some user errors
* like < and < in attribute values or inside text nodes.
*
* @package phptal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
* @see PHPTAL_Dom_Parser
*/
abstract class PHPTAL_XmlParser
{
// available parser states
const ST_ROOT = 0;
const ST_TEXT = 1;
const ST_LT = 2;
const ST_TAG_NAME = 3;
const ST_TAG_CLOSE = 4;
const ST_TAG_SINGLE = 5;
const ST_TAG_ATTRIBUTES = 6;
const ST_CDATA = 7;
const ST_COMMENT = 8;
const ST_DOCTYPE = 9;
const ST_XMLDEC = 15;
const ST_PREPROC = 10;
const ST_ATTR_KEY = 11;
const ST_ATTR_EQ = 12;
const ST_ATTR_QUOTE = 13;
const ST_ATTR_VALUE = 14;
// exceptions error messages
const ERR_CHARS_BEFORE_DOC_START =
"Characters found before the begining of the document!";
const ERR_EXPECT_VALUE_QUOTE =
"Unexpected '%s' character, expecting attribute single or double quote";
const BOM_STR = "\xef\xbb\xbf";
static $state_names = array(
self::ST_ROOT => 'root node',
self::ST_TEXT => 'text',
self::ST_LT => 'start of tag',
self::ST_TAG_NAME => 'tag name',
self::ST_TAG_CLOSE => 'closing tag',
self::ST_TAG_SINGLE => 'self-closing tag',
self::ST_TAG_ATTRIBUTES => 'tag',
self::ST_CDATA => 'CDATA',
self::ST_COMMENT => 'comment',
self::ST_DOCTYPE => 'doctype',
self::ST_XMLDEC => 'XML declaration',
self::ST_PREPROC => 'preprocessor directive',
self::ST_ATTR_KEY => 'attribute name',
self::ST_ATTR_EQ => 'attribute value',
self::ST_ATTR_QUOTE => 'quoted attribute value',
self::ST_ATTR_VALUE => 'unquoted attribute value',
);
public function __construct()
{
$this->_file = "<string>";
}
public function parseFile($src)
{
if (!file_exists($src)) {
throw new PHPTAL_Exception("file $src not found");
}
$this->parseString(file_get_contents($src), $src);
}
public function parseString($src, $filename = '<string>')
{
$this->_file = $filename;
// remove BOM (utf8 byte order mark)...
if (substr($src,0,3) == self::BOM_STR){
$src = substr($src, 3);
}
$this->_line = 1;
$state = self::ST_ROOT;
$mark = 0;
$len = strlen($src);
$quoteStyle = '"';
$tagname = "";
$attribute = "";
$attributes = array();
$customDoctype = false;
$this->onDocumentStart();
for ($i=0; $i<$len; $i++) {
$c = $src[$i];
if ($c == "\n") $this->_line++;
switch ($state) {
case self::ST_ROOT:
if ($c == '<') {
$mark = $i; // mark tag start
$state = self::ST_LT;
}
else if (!self::isWhiteChar($c)) {
$this->raiseError(self::ERR_CHARS_BEFORE_DOC_START);
}
break;
case self::ST_TEXT:
if ($c == '<') {
if ($mark != $i) {
$this->onElementData(substr($src, $mark, $i-$mark));
}
$mark = $i;
$state = self::ST_LT;
}
break;
case self::ST_LT:
if ($c == '/') {
$mark = $i+1;
$state = self::ST_TAG_CLOSE;
}
else if ($c == '?' and substr($src, $i, 4) == '?xml') {
$state = self::ST_XMLDEC;
}
else if ($c == '?') {
$state = self::ST_PREPROC;
}
else if ($c == '!' and substr($src, $i, 3) == '!--') {
$state = self::ST_COMMENT;
}
else if ($c == '!' and substr($src, $i, 8) == '![CDATA[') {
$state = self::ST_CDATA;
}
else if ($c == '!' and substr($src, $i, 8) == '!DOCTYPE') {
$state = self::ST_DOCTYPE;
}
else if (!self::isAlpha($c)) {
$state = self::ST_TEXT;
}
else {
$mark = $i; // mark node name start
$attributes = array();
$attribute = "";
$state = self::ST_TAG_NAME;
}
break;
case self::ST_TAG_NAME:
if (self::isWhiteChar($c)) {
$tagname = substr($src, $mark, $i-$mark);
$state = self::ST_TAG_ATTRIBUTES;
}
else if ($c == '/') {
$tagname = substr($src, $mark, $i-$mark);
$state = self::ST_TAG_SINGLE;
}
else if ($c == '>') {
$tagname = substr($src, $mark, $i-$mark);
$mark = $i+1; // mark text start
$state = self::ST_TEXT;
$this->onElementStart($tagname, $attributes);
}
break;
case self::ST_TAG_CLOSE:
if ($c == '>') {
$tagname = rtrim(substr($src, $mark, $i-$mark));
$this->onElementClose($tagname);
$mark = $i+1; // mark text start
$state = self::ST_TEXT;
}
break;
case self::ST_TAG_SINGLE:
if ($c != '>') {
// error
}
$mark = $i+1; // mark text start
$state = self::ST_TEXT;
$this->onElementStart($tagname, $attributes);
$this->onElementClose($tagname);
break;
case self::ST_TAG_ATTRIBUTES:
if ($c == '>') {
$mark = $i+1; // mark text start
$state = self::ST_TEXT;
$this->onElementStart($tagname, $attributes);
}
else if ($c == '/') {
$state = self::ST_TAG_SINGLE;
}
else if (self::isWhiteChar($c)) {
}
else {
$mark = $i; // mark attribute key start
$state = self::ST_ATTR_KEY;
}
break;
case self::ST_COMMENT:
if ($c == '>' and substr($src, $i-2, 2) == '--') {
$this->onComment(substr($src, $mark, $i-$mark+1));
$mark = $i+1; // mark text start
$state = self::ST_TEXT;
}
break;
case self::ST_CDATA:
if ($c == '>' and substr($src, $i-2, 2) == ']]') {
$this->onSpecific(substr($src, $mark, $i-$mark+1));
$mark = $i+1; // mark text start
$state = self::ST_TEXT;
}
break;
case self::ST_XMLDEC:
if ($c == '?' && substr($src, $i, 2) == '?>') {
$this->onXmlDecl(substr($src, $mark, $i-$mark+2));
$i++; // skip '>'
$mark = $i+1; // mark text start
$state = self::ST_TEXT;
}
break;
case self::ST_DOCTYPE:
if ($c == '[') {
$customDoctype = true;
}
else if ($customDoctype && $c == '>' && substr($src, $i-1, 2) == ']>'){
$customDoctype = false;
$this->onDocType(substr($src, $mark, $i-$mark+1));
$mark = $i+1; // mark text start
$state = self::ST_TEXT;
}
else if (!$customDoctype && $c == '>') {
$customDoctype = false;
$this->onDocType(substr($src, $mark, $i-$mark+1));
$mark = $i+1; // mark text start
$state = self::ST_TEXT;
}
break;
case self::ST_PREPROC:
if ($c == '>' and $src[$i-1] == '?') {
$this->onSpecific(substr($src, $mark, $i-$mark+1));
$mark = $i+1; // mark text start
$state = self::ST_TEXT;
}
break;
case self::ST_ATTR_KEY:
if (self::isWhiteChar($c)) {
$attribute = substr($src, $mark, $i-$mark);
$state = self::ST_ATTR_EQ;
}
else if ($c == '=') {
$attribute = substr($src, $mark, $i-$mark);
$state = self::ST_ATTR_VALUE;
}
break;
case self::ST_ATTR_EQ:
if ($c == '=') {
$state = self::ST_ATTR_VALUE;
}
break;
case self::ST_ATTR_VALUE:
if (self::isWhiteChar($c)){
}
else if ($c == '"' or $c == '\'') {
$quoteStyle = $c;
$state = self::ST_ATTR_QUOTE;
$mark = $i+1; // mark attribute real value start
}
else {
$err = self::ERR_EXPECT_VALUE_QUOTE;
$err = sprintf($err, $c);
$this->raiseError($err);
}
break;
case self::ST_ATTR_QUOTE:
if ($c == $quoteStyle) {
if (isset($attributes[$attribute])) $this->raiseError("Attribute '$attribute' on '$tagname' is defined more than once");
$attributes[$attribute] = substr($src, $mark, $i-$mark);
$state = self::ST_TAG_ATTRIBUTES;
}
break;
}
}
if ($state == self::ST_TEXT) // allows text past root node, which is in violation of XML spec
{
if ($i > $mark)
{
$text = substr($src, $mark, $i-$mark);
//if (!ctype_space($text)) $this->onElementData($text);
if (!ctype_space($text)) $this->raiseError("Characters found after end of the root element");
}
}
else
{
throw new PHPTAL_Exception("Finished document in unexpected state: ".self::$state_names[$state]." is not finished");
}
$this->onDocumentEnd();
}
public function getSourceFile()
{
return $this->_file;
}
public function getLineNumber()
{
return $this->_line;
}
public static function isWhiteChar($c)
{
return strpos(" \t\n\r\0", $c) !== false;
}
public static function isAlpha($c)
{
$char = strtolower($c);
return ($char >= 'a' && $char <= 'z');
}
public abstract function onDocType($doctype);
public abstract function onXmlDecl($decl);
public abstract function onSpecific($data);
public abstract function onComment($data);
public abstract function onElementStart($name, $attributes);
public abstract function onElementClose($name);
public abstract function onElementData($data);
public abstract function onDocumentStart();
public abstract function onDocumentEnd();
protected function raiseError($errFmt)
{
$args = func_get_args();
$errStr = call_user_func_array('sprintf', $args);
$str = "%s error: %s in %s:%d";
$str = sprintf($str, get_class($this), $errStr, $this->_file, $this->_line);
throw new PHPTAL_Exception($str);
}
private $_file;
private $_line;
private $_source;
}
?>

98
PHPTAL/Dom/XmlnsState.php Normal file
View file

@ -0,0 +1,98 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* Stores XMLNS aliases fluctuation in the xml flow.
*
* This class is used to bind a PHPTAL namespace to an alias, for example using
* xmlns:t="http://xml.zope.org/namespaces/tal" and later use t:repeat instead
* of tal:repeat.
*
* @package phptal.dom
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Dom_XmlnsState
{
/** Create a new XMLNS state inheriting provided aliases. */
public function __construct($aliases = array())
{
assert(is_array($aliases));
$this->_aliases = $aliases;
}
/** Returns true if $attName is a valid attribute name, false otherwise. */
public function isValidAttribute($attName)
{
$unaliased = $this->unAliasAttribute($attName);
return PHPTAL_Dom_Defs::getInstance()->isValidAttribute($unaliased);
}
/** Returns true if $attName is a PHPTAL attribute, false otherwise. */
public function isPhpTalAttribute($attName)
{
$unaliased = $this->unAliasAttribute($attName);
return PHPTAL_Dom_Defs::getInstance()->isPhpTalAttribute($unaliased);
}
/** Returns the unaliased name of specified attribute. */
public function unAliasAttribute($attName)
{
if (count($this->_aliases) == 0)
return $attName;
$result = $attName;
foreach ($this->_aliases as $alias => $real){
$result = str_replace("$alias:", "$real:", $result);
}
return $result;
}
/**
* Returns a new XmlnsState inheriting of $currentState if $nodeAttributes contains
* xmlns attributes, returns $currentState otherwise.
*
* This method is used by the PHPTAL parser to keep track of xmlns fluctuation for
* each encountered node.
*/
public static function newElement(PHPTAL_Dom_XmlnsState $currentState, $nodeAttributes)
{
$aliases = array();
foreach ($nodeAttributes as $att => $value){
if (PHPTAL_Dom_Defs::getInstance()->isHandledXmlNs($att, $value)){
preg_match('/^xmlns:(.*?)$/', $att, $m);
list(,$alias) = $m;
$aliases[$alias] = PHPTAL_Dom_Defs::getInstance()->xmlnsToLocalName($value);
}
}
if (count($aliases) > 0){
// inherit aliases with maybe an overwrite
$aliases = array_merge($currentState->_aliases, $aliases);
return new PHPTAL_Dom_XmlnsState($aliases);
}
return $currentState;
}
private $_aliases;
}
?>

65
PHPTAL/Exception.php Normal file
View file

@ -0,0 +1,65 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* @package phptal
*/
class PHPTAL_Exception extends Exception
{
public $srcFile;
public $srcLine;
public function __construct($msg, $srcFile=false, $srcLine=false)
{
parent::__construct($msg);
$this->srcFile = $srcFile;
$this->srcLine = $srcLine;
}
public function __toString()
{
if (empty($this->srcFile)){
return parent::__toString();
}
$res = sprintf('From %s around line %d'."\n", $this->srcFile, $this->srcLine);
$res .= parent::__toString();
return $res;
}
public static function formatted($format /*, ...*/)
{
$args = func_get_args();
$msg = call_user_func('sprintf', $args);
return new PHPTAL_Exception($format);
}
/**
* set new source line/file only if one hasn't been set previously
*/
public function hintSrcPosition($srcFile, $srcLine)
{
if ($srcFile && $this->srcFile === false) $this->srcFile = $srcFile;
if ($srcLine && $this->srcLine === false) $this->srcLine = $srcLine;
}
}
?>

64
PHPTAL/FileSource.php Normal file
View file

@ -0,0 +1,64 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Source.php';
require_once PHPTAL_DIR.'PHPTAL/SourceResolver.php';
/**
* @package phptal
*/
class PHPTAL_FileSource implements PHPTAL_Source
{
public function __construct($path)
{
$this->_path = realpath($path);
if ($this->_path === false) throw new PHPTAL_Exception("Unable to normalize path '$path'");
}
public function getRealPath()
{
return $this->_path;
}
public function getLastModifiedTime()
{
return filemtime($this->_path);
}
public function getData()
{
return file_get_contents($this->_path);
}
private $_path;
}
/**
* @package phptal
*/
class PHPTAL_FileSourceResolver implements PHPTAL_SourceResolver
{
public function __construct($repositories)
{
$this->_repositories = $repositories;
}
public function resolve($path)
{
foreach ($this->_repositories as $repository){
$file = $repository . DIRECTORY_SEPARATOR . $path;
if (file_exists($file)){
return new PHPTAL_FileSource($file);
}
}
if (file_exists($path)){
return new PHPTAL_FileSource($path);
}
return null;
}
private $_repositories;
}
?>

31
PHPTAL/Filter.php Normal file
View file

@ -0,0 +1,31 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* @package phptal
*/
interface PHPTAL_Filter
{
public function filter($str);
}
?>

View file

@ -0,0 +1,143 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/TranslationService.php';
/**
* PHPTAL_TranslationService gettext implementation.
*
* Because gettext is the most common translation library in use, this
* implementation is shipped with the PHPTAL library.
*
* Please refer to the PHPTAL documentation for usage examples.
*
* @package phptal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_GetTextTranslator implements PHPTAL_TranslationService
{
public function __construct()
{
if (!function_exists('gettext')) throw new PHPTAL_Exception("Gettext not installed");
$this->useDomain("messages"); // PHP bug #21965
}
private $_vars = array();
private $_currentDomain;
private $_encoding = 'UTF-8';
private $_canonicalize = false;
public function setEncoding($enc)
{
$this->_encoding = $enc;
}
/**
* if true, all non-ASCII characters in keys will be converted to C<xxx> form. This impacts performance.
* by default keys will be passed to gettext unmodified.
*/
public function setCanonicalize($bool)
{
$this->_canonicalize = $bool;
}
public function setLanguage()
{
$langs = func_get_args();
foreach ($langs as $langCode){
putenv("LANG=$langCode");
putenv("LC_ALL=$langCode");
putenv("LANGUAGE=$langCode");
if (setlocale(LC_ALL, $langCode)) {
return;
}
}
$err = sprintf('Language(s) code(s) "%s" not supported by your system', join(',', $langs));
throw new PHPTAL_Exception($err);
}
/**
* encoding must be set before calling addDomain
*/
public function addDomain($domain, $path='./locale/')
{
bindtextdomain($domain, $path);
if ($this->_encoding){
bind_textdomain_codeset($domain, $this->_encoding);
}
$this->useDomain($domain);
}
public function useDomain($domain)
{
$old = $this->_currentDomain;
$this->_currentDomain = $domain;
textdomain($domain);
return $old;
}
public function setVar($key, $value)
{
$this->_vars[$key] = $value;
}
public function translate($key, $htmlencode=true)
{
if ($this->_canonicalize) $key = self::_canonicalizeKey($key);
$value = gettext($key);
if ($htmlencode){
$value = @htmlspecialchars($value, ENT_QUOTES, $this->_encoding); // silence unsupported encoding error for ISO-8859-x, which doesn't matter.
}
while (preg_match('/\${(.*?)\}/sm', $value, $m)){
list($src,$var) = $m;
if (!array_key_exists($var, $this->_vars)){
$err = sprintf('Interpolation error, var "%s" not set', $var);
throw new PHPTAL_Exception($err);
}
$value = str_replace($src, $this->_vars[$var], $value);
}
return $value;
}
static function _canonicalizeKey($key_)
{
$result = "";
$key_ = trim($key_);
$key_ = str_replace("\n", "", $key_);
$key_ = str_replace("\r", "", $key_);
for ($i = 0; $i<strlen($key_); $i++){
$c = $key_[$i];
$o = ord($c);
if ($o < 5 || $o > 127){
$result .= 'C<'.$o.'>';
}
else {
$result .= $c;
}
}
return $result;
}
}

163
PHPTAL/Namespace.php Normal file
View file

@ -0,0 +1,163 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* @package phptal
*/
abstract class PHPTAL_NamespaceAttribute
{
/**
* @param $name string The attribute name
* @param $priority int Attribute execution priority
*/
public function __construct($name, $priority)
{
$this->_name = $name;
$this->_priority = $priority;
}
/**
* @return string
*/
public function getName()
{
return $this->_name;
}
public function getFullName()
{
return $this->_namespace->getName() . ':' . $this->_name;
}
public function getPriority(){ return $this->_priority; }
public function getNamespace(){ return $this->_namespace; }
public function setNamespace(PHPTAL_Namespace $ns){ $this->_namespace = $ns; }
public function createAttributeHandler(PHPTAL_Php_Element $tag, $expression)
{
return $this->_namespace->createAttributeHandler($this, $tag, $expression);
}
private $_name; /* Attribute name without the namespace: prefix */
private $_priority; /* [0 - 1000] */
private $_namespace; /* PHPTAL_Namespace */
}
/**
* @package phptal
*/
class PHPTAL_NamespaceAttributeSurround extends PHPTAL_NamespaceAttribute
{
public function __construct($name, $priority)
{
parent::__construct($name, $priority);
}
}
/**
* @package phptal
*/
class PHPTAL_NamespaceAttributeReplace extends PHPTAL_NamespaceAttribute
{
public function __construct($name, $priority)
{
parent::__construct($name, $priority);
}
}
/**
* @package phptal
*/
class PHPTAL_NamespaceAttributeContent extends PHPTAL_NamespaceAttribute
{
public function __construct($name, $priority)
{
parent::__construct($name, $priority);
}
}
/**
* @package phptal
*/
abstract class PHPTAL_Namespace
{
public $xmlns;
public $name;
public function __construct($name, $xmlns)
{
$this->_attributes = array();
$this->name = $name;
$this->xmlns = $xmlns;
}
public function getName()
{
return $this->name;
}
public function hasAttribute($attributeName)
{
return array_key_exists(strtolower($attributeName), $this->_attributes);
}
public function getAttribute($attributeName)
{
return $this->_attributes[strtolower($attributeName)];
}
public function addAttribute(PHPTAL_NamespaceAttribute $attribute)
{
$attribute->setNamespace($this);
$this->_attributes[strtolower($attribute->getName())] = $attribute;
}
public function getAttributes()
{
return $this->_attributes;
}
abstract public function createAttributeHandler(PHPTAL_NamespaceAttribute $att, PHPTAL_Php_Element $tag, $expression);
protected $_attributes;
}
/**
* @package phptal
*/
class PHPTAL_BuiltinNamespace extends PHPTAL_Namespace
{
public function createAttributeHandler(PHPTAL_NamespaceAttribute $att, PHPTAL_Php_Element $tag, $expression)
{
$name = $att->getName();
$name = str_replace('-', '', $name);
$class = 'PHPTAL_Php_Attribute_'.$this->getName().'_'.$name;
$result = new $class();
$result->tag = $tag;
$result->name = strtoupper($att->getFullName());
$result->expression = $expression;
return $result;
}
}
?>

28
PHPTAL/Namespace/I18N.php Normal file
View file

@ -0,0 +1,28 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Dom/Defs.php';
require_once PHPTAL_DIR.'PHPTAL/Namespace.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/I18N/Translate.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/I18N/Name.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/I18N/Domain.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/I18N/Attributes.php';
/**
* @package phptal.namespace
*/
class PHPTAL_Namespace_I18N extends PHPTAL_BuiltinNamespace
{
public function __construct()
{
parent::__construct('i18n', 'http://xml.zope.org/namespaces/i18n');
$this->addAttribute(new PHPTAL_NamespaceAttributeContent('translate', 5));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('name', 5));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('attributes', 10));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('domain', 3));
}
}
PHPTAL_Dom_Defs::getInstance()->registerNamespace(new PHPTAL_Namespace_I18N());
?>

View file

@ -0,0 +1,28 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Dom/Defs.php';
require_once PHPTAL_DIR.'PHPTAL/Namespace.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/METAL/DefineMacro.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/METAL/UseMacro.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/METAL/DefineSlot.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/METAL/FillSlot.php';
/**
* @package phptal.namespace
*/
class PHPTAL_Namespace_METAL extends PHPTAL_BuiltinNamespace
{
public function __construct()
{
parent::__construct('metal', 'http://xml.zope.org/namespaces/metal');
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('define-macro', 1));
$this->addAttribute(new PHPTAL_NamespaceAttributeReplace('use-macro', 9));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('define-slot', 9));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('fill-slot', 9));
}
}
PHPTAL_Dom_Defs::getInstance()->registerNamespace(new PHPTAL_Namespace_METAL());
?>

View file

@ -0,0 +1,28 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Dom/Defs.php';
require_once PHPTAL_DIR.'PHPTAL/Namespace.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/PHPTAL/Tales.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/PHPTAL/Debug.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/PHPTAL/Id.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/PHPTAL/Cache.php';
/**
* @package phptal.namespace
*/
class PHPTAL_Namespace_PHPTAL extends PHPTAL_BuiltinNamespace
{
public function __construct()
{
parent::__construct('phptal', 'http://xml.zope.org/namespaces/phptal');
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('tales', -1));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('debug', -2));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('id', 7));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('cache', -3));
}
}
PHPTAL_Dom_Defs::getInstance()->registerNamespace(new PHPTAL_Namespace_PHPTAL());
?>

37
PHPTAL/Namespace/TAL.php Normal file
View file

@ -0,0 +1,37 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Namespace.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/TAL/Comment.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/TAL/Replace.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/TAL/Content.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/TAL/Condition.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/TAL/Attributes.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/TAL/Repeat.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/TAL/Define.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/TAL/OnError.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute/TAL/OmitTag.php';
/**
* @package phptal.namespace
*/
class PHPTAL_Namespace_TAL extends PHPTAL_BuiltinNamespace
{
public function __construct()
{
parent::__construct('tal', 'http://xml.zope.org/namespaces/tal');
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('define', 4));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('condition', 6));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('repeat', 8));
$this->addAttribute(new PHPTAL_NamespaceAttributeContent('content', 11));
$this->addAttribute(new PHPTAL_NamespaceAttributeReplace('replace', 9));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('attributes', 9));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('omit-tag', 0));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('comment', 12));
$this->addAttribute(new PHPTAL_NamespaceAttributeSurround('on-error', 2));
}
}
PHPTAL_Dom_Defs::getInstance()->registerNamespace(new PHPTAL_Namespace_TAL());
?>

98
PHPTAL/Php/Attribute.php Normal file
View file

@ -0,0 +1,98 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Dom/Node.php';
/**
* Base class for all PHPTAL attributes.
*
* Attributes are first ordered by PHPTAL then called depending on their
* priority before and after the element printing.
*
* An attribute must implements start() and end().
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
abstract class PHPTAL_Php_Attribute
{
const ECHO_TEXT = 'text';
const ECHO_STRUCTURE = 'structure';
/** Attribute name (ie: 'tal:content'). */
public $name;
/** Attribute value specified by the element. */
public $expression;
/** Element using this attribute (xml node). */
public $tag;
/** Called before element printing. */
public abstract function start();
/** Called after element printing. */
public abstract function end();
/**
* Remove structure|text keyword from expression and stores it for later
* doEcho() usage.
*
* $expression = 'stucture my/path';
* $expression = $this->extractEchoType($expression);
*
* ...
*
* $this->doEcho($code);
*/
protected function extractEchoType($expression)
{
$echoType = self::ECHO_TEXT;
$expression = trim($expression);
if (preg_match('/^(text|structure)\s+(.*?)$/ism', $expression, $m)) {
list(, $echoType, $expression) = $m;
}
$this->_echoType = strtolower($echoType);
return trim($expression);
}
protected function doEcho($code)
{
if ($this->_echoType == self::ECHO_TEXT)
$this->tag->generator->doEcho($code);
else
$this->tag->generator->doEchoRaw($code);
}
protected function parseSetExpression($exp)
{
$exp = trim($exp);
// (dest) (value)
if (preg_match('/^([a-z0-9:\-_]+)\s+(.*?)$/i', $exp, $m)){
array_shift($m);
return $m;
}
// (dest)
return array($exp, null);
}
protected $_echoType = PHPTAL_Php_Attribute::ECHO_TEXT;
}
?>

View file

@ -0,0 +1,107 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:attributes
//
// This attribute will allow us to translate attributes of HTML tags, such
// as the alt attribute in the img tag. The i18n:attributes attribute
// specifies a list of attributes to be translated with optional message
// IDs? for each; if multiple attribute names are given, they must be
// separated by semi-colons. Message IDs? used in this context must not
// include whitespace.
//
// Note that the value of the particular attributes come either from the
// HTML attribute value itself or from the data inserted by tal:attributes.
//
// If an attibute is to be both computed using tal:attributes and translated,
// the translation service is passed the result of the TALES expression for
// that attribute.
//
// An example:
//
// <img src="http://foo.com/logo" alt="Visit us"
// tal:attributes="alt here/greeting"
// i18n:attributes="alt"
// />
//
//
// In this example, let tal:attributes set the value of the alt attribute to
// the text "Stop by for a visit!". This text will be passed to the
// translation service, which uses the result of language negotiation to
// translate "Stop by for a visit!" into the requested language. The example
// text in the template, "Visit us", will simply be discarded.
//
// Another example, with explicit message IDs:
//
// <img src="../icons/uparrow.png" alt="Up"
// i18n:attributes="src up-arrow-icon; alt up-arrow-alttext"
// >
//
// Here, the message ID up-arrow-icon will be used to generate the link to
// an icon image file, and the message ID up-arrow-alttext will be used for
// the "alt" text.
//
/**
* @package phptal.php.attribute
*/
class PHPTAL_Php_Attribute_I18N_Attributes extends PHPTAL_Php_Attribute
{
public function start()
{
// split attributes to translate
$expressions = $this->tag->generator->splitExpression($this->expression);
// foreach attribute
foreach ($expressions as $exp){
list($attribute, $key) = $this->parseSetExpression($exp);
// if the translation key is specified
if ($key != null){
// we use it and replace the tag attribute with the result of
// the translation
$key = str_replace('\'', '\\\'', $key);
$this->tag->attributes[$attribute] = $this->_getTranslationCode("'$key'");
}
else if ($this->tag->isOverwrittenAttribute($attribute)){
$varn = $this->tag->getOverwrittenAttributeVarName($attribute);
$this->tag->attributes[$attribute] = $this->_getTranslationCode($varn);
}
// else if the attribute has a default value
else if ($this->tag->hasAttribute($attribute)){
// we use this default value as the translation key
$key = $this->tag->getAttribute($attribute);
$key = str_replace('\'', '\\\'', $key);
$this->tag->attributes[$attribute] = $this->_getTranslationCode("'$key'");
}
else {
// unable to translate the attribute
throw new PHPTAL_Exception("Unable to translate attribute $attribute");
}
}
}
public function end()
{
}
private function _getTranslationCode($key)
{
$code = '<?php ';
if (preg_match_all('/\$\{(.*?)\}/', $key, $m)){
array_shift($m);
$m = array_shift($m);
foreach ($m as $name){
$code .= "\n".'$_translator->setVar(\''.$name.'\','.phptal_tale($name).');'; // allow more complex TAL expressions
}
$code .= "\n";
}
// notice the false boolean which indicate that the html is escaped
// elsewhere looks like an hack doesn't it ? :)
$result = $this->tag->generator->escapeCode(sprintf('$_translator->translate(%s, false)', $key));
$code .= 'echo '.$result.'?>';
return $code;
}
}
?>

View file

@ -0,0 +1,24 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:data
//
// Since TAL always returns strings, we need a way in ZPT to translate
// objects, the most obvious case being DateTime objects. The data attribute
// will allow us to specify such an object, and i18n:translate will provide
// us with a legal format string for that object. If data is used,
// i18n:translate must be used to give an explicit message ID, rather than
// relying on a message ID computed from the content.
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Data extends PHPTAL_Php_Attribute
{
public function start(){}
public function end(){}
}
?>

View file

@ -0,0 +1,41 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:domain
//
// The i18n:domain attribute is used to specify the domain to be used to get
// the translation. If not specified, the translation services will use a
// default domain. The value of the attribute is used directly; it is not
// a TALES expression.
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Domain extends PHPTAL_Php_Attribute
{
public function start()
{
// ensure a domain stack exists or create it
$this->tag->generator->doIf('!isset($__i18n_domains)');
$this->tag->generator->pushCode('$__i18n_domains = array()');
$this->tag->generator->doEnd();
//\''.str_replace(array('\\',"'"),array('\\\\',"\\'"),$expression).'\'
$expression = $this->tag->generator->interpolateTalesVarsInString($this->expression);
// push current domain and use new domain
$code = '$__i18n_domains[] = $_translator->useDomain('.$expression.')';
$this->tag->generator->pushCode($code);
}
public function end()
{
// restore domain
$code = '$_translator->useDomain(array_pop($__i18n_domains))';
$this->tag->generator->pushCode($code);
}
}
?>

View file

@ -0,0 +1,40 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:name
//
// Name the content of the current element for use in interpolation within
// translated content. This allows a replaceable component in content to be
// re-ordered by translation. For example:
//
// <span i18n:translate=''>
// <span tal:replace='here/name' i18n:name='name' /> was born in
// <span tal:replace='here/country_of_birth' i18n:name='country' />.
// </span>
//
// would cause this text to be passed to the translation service:
//
// "${name} was born in ${country}."
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Name extends PHPTAL_Php_Attribute
{
public function start()
{
$this->tag->generator->pushCode('ob_start()');
}
public function end()
{
$code = '$_translator->setVar(\'%s\', ob_get_contents())';
$code = sprintf($code, $this->expression);
$this->tag->generator->pushCode($code);
$this->tag->generator->pushCode('ob_end_clean()');
}
}
?>

View file

@ -0,0 +1,38 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:source
//
// The i18n:source attribute specifies the language of the text to be
// translated. The default is "nothing", which means we don't provide
// this information to the translation services.
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Source extends PHPTAL_Php_Attribute
{
public function start()
{
// ensure that a sources stack exists or create it
$this->tag->generator->doIf('!isset($__i18n_sources)');
$this->tag->generator->pushCode('$__i18n_sources = array()');
$this->tag->generator->end();
// push current source and use new one
$code = '$__i18n_sources[] = $_translator->setSource(\'%s\')';
$code = sprintf($code, $this->expression);
$this->tag->generator->pushCode($code);
}
public function end()
{
// restore source
$code = '$_translator->setSource(array_pop($__i18n_sources))';
$this->tag->generator->pushCode($code);
}
}
?>

View file

@ -0,0 +1,32 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:target
//
// The i18n:target attribute specifies the language of the translation we
// want to get. If the value is "default", the language negotiation services
// will be used to choose the destination language. If the value is
// "nothing", no translation will be performed; this can be used to suppress
// translation within a larger translated unit. Any other value must be a
// language code.
//
// The attribute value is a TALES expression; the result of evaluating the
// expression is the language code or one of the reserved values.
//
// Note that i18n:target is primarily used for hints to text extraction
// tools and translation teams. If you had some text that should only be
// translated to e.g. German, then it probably shouldn't be wrapped in an
// i18n:translate span.
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Target extends PHPTAL_Php_Attribute
{
public function start(){}
public function end(){}
}
?>

View file

@ -0,0 +1,97 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// ZPTInternationalizationSupport
//
// i18n:translate
//
// This attribute is used to mark units of text for translation. If this
// attribute is specified with an empty string as the value, the message ID
// is computed from the content of the element bearing this attribute.
// Otherwise, the value of the element gives the message ID.
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Translate extends PHPTAL_Php_Attribute
{
public function start()
{
$escape = true;
if (preg_match('/^(text|structure)(?:\s+(.*)|\s*$)/',$this->expression,$m))
{
if ($m[1]=='structure') $escape=false;
$this->expression = isset($m[2])?$m[2]:'';
}
// if no expression is given, the content of the node is used as
// a translation key
if (strlen(trim($this->expression)) == 0){
$key = $this->_getTranslationKey($this->tag, !$escape);
$key = trim(preg_replace('/\s+/sm'.($this->tag->generator->getEncoding()=='UTF-8'?'u':''), ' ', $key));
$code = '\'' . str_replace('\'', '\\\'', $key) . '\'';
}
else {
$code = $this->tag->generator->evaluateExpression($this->expression);
}
$this->_prepareNames($this->tag);
$php = sprintf('echo $_translator->translate(%s,%s);', $code, $escape ? 'true':'false');
$this->tag->generator->pushCode($php);
}
public function end()
{
}
private function _getTranslationKey($tag, $preserve_tags)
{
$result = '';
foreach ($tag->children as $child){
if ($child instanceOf PHPTAL_Php_Text){
$result .= $child->node->getValue();
}
else if ($child instanceOf PHPTAL_Php_Element){
if ($child->hasAttribute('i18n:name')){
$value = $child->getAttribute('i18n:name');
$result .= '${' . $value . '}';
}
else {
if ($preserve_tags)
{
$result .= '<'.$child->name;
foreach($child->attributes as $k => $v)
{
$result .= ' '.$k.'="'.$v.'"';
}
$result .= '>'.$this->_getTranslationKey($child, $preserve_tags).'</'.$child->name.'>';
}
else
{
$result .= $this->_getTranslationKey($child, $preserve_tags);
}
}
}
}
return $result;
}
private function _prepareNames($tag)
{
foreach ($tag->children as $child){
if ($child instanceOf PHPTAL_Php_Element){
if ($child->hasAttribute('i18n:name')){
$child->generate();
}
else {
$this->_prepareNames($child);
}
}
}
}
}
?>

View file

@ -0,0 +1,71 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// METAL Specification 1.0
//
// argument ::= Name
//
// Example:
//
// <p metal:define-macro="copyright">
// Copyright 2001, <em>Foobar</em> Inc.
// </p>
//
// PHPTAL:
//
// <?php function XXX_macro_copyright( $tpl ) { ? >
// <p>
// Copyright 2001, <em>Foobar</em> Inc.
// </p>
// <?php } ? >
//
/**
* @package phptal.php.attribute.metal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_METAL_DefineMacro extends PHPTAL_Php_Attribute
{
public function start()
{
$macroname = strtr(trim($this->expression),'-','_');
if (!preg_match('/^[a-z0-9_]+$/i', $macroname)){
throw new PHPTAL_Exception('Bad macro name "'.$macroname.'"', $this->tag->getSourceFile(), $this->tag->getSourceLine());
}
$this->tag->generator->doFunction($macroname, '$tpl, $ctx');
$this->tag->generator->doXmlDeclaration();
$this->tag->generator->doDoctype();
$this->tag->generator->doSetVar('$tpl', 'clone $tpl');
$this->tag->generator->doSetVar('$ctx', '$tpl->getContext()');
$this->tag->generator->doSetVar('$glb', '$tpl->getGlobalContext()');
$this->tag->generator->doSetVar('$_translator', '$tpl->getTranslator()');
}
public function end()
{
$this->tag->generator->doEnd();
}
}

View file

@ -0,0 +1,75 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// METAL Specification 1.0
//
// argument ::= Name
//
// Example:
//
// <table metal:define-macro="sidebar">
// <tr><th>Links</th></tr>
// <tr><td metal:define-slot="links">
// <a href="/">A Link</a>
// </td></tr>
// </table>
//
// PHPTAL: (access to slots may be renamed)
//
// <?php function XXXX_macro_sidebar( $tpl ) { ? >
// <table>
// <tr><th>Links</th></tr>
// <tr>
// <?php if (isset($tpl->slots->links)): ? >
// <?php echo $tpl->slots->links ? >
// <?php else: ? >
// <td>
// <a href="/">A Link</a>
// </td></tr>
// </table>
// <?php } ? >
//
/**
* @package phptal.php.attribute.metal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_METAL_DefineSlot extends PHPTAL_Php_Attribute
{
public function start()
{
$cond = sprintf('$ctx->hasSlot("%s")', $this->expression);
$this->tag->generator->doIf($cond);
$code = sprintf('<?php echo $ctx->getSlot("%s") ?>', $this->expression);
$this->tag->generator->pushHtml($code);
$this->tag->generator->doElse();
}
public function end()
{
$this->tag->generator->doEnd();
}
}
?>

View file

@ -0,0 +1,75 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// METAL Specification 1.0
//
// argument ::= Name
//
// Example:
//
// <table metal:use-macro="here/doc1/macros/sidebar">
// <tr><th>Links</th></tr>
// <tr><td metal:fill-slot="links">
// <a href="http://www.goodplace.com">Good Place</a><br>
// <a href="http://www.badplace.com">Bad Place</a><br>
// <a href="http://www.otherplace.com">Other Place</a>
// </td></tr>
// </table>
//
// PHPTAL:
//
// 1. evaluate slots
//
// <?php ob_start(); ? >
// <td>
// <a href="http://www.goodplace.com">Good Place</a><br>
// <a href="http://www.badplace.com">Bad Place</a><br>
// <a href="http://www.otherplace.com">Other Place</a>
// </td>
// <?php $tpl->slots->links = ob_get_contents(); ob_end_clean(); ? >
//
// 2. call the macro (here not supported)
//
// <?php echo phptal_macro($tpl, 'master_page.html/macros/sidebar'); ? >
//
/**
* @package phptal.php.attribute.metal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_METAL_FillSlot extends PHPTAL_Php_Attribute
{
public function start()
{
$this->tag->generator->pushCode('ob_start()');
}
public function end()
{
$code = '$ctx->fillSlot("'.$this->expression.'", ob_get_clean())';
$this->tag->generator->pushCode($code);
}
}

View file

@ -0,0 +1,131 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// METAL Specification 1.0
//
// argument ::= expression
//
// Example:
//
// <hr />
// <p metal:use-macro="here/master_page/macros/copyright">
// <hr />
//
// PHPTAL: (here not supported)
//
// <?php echo phptal_macro( $tpl, 'master_page.html/macros/copyright'); ? >
//
/**
* @package phptal.php.attribute.metal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_METAL_UseMacro extends PHPTAL_Php_Attribute
{
static $ALLOWED_ATTRIBUTES = array(
'metal:fill-slot',
'metal:define-macro',
'tal:define',
);
public function start()
{
$this->pushSlots();
foreach ($this->tag->children as $child){
$this->generateFillSlots($child);
}
$macroname = strtr($this->expression,'-','_');
// local macro (no filename specified) and non dynamic macro name
if (preg_match('/^[a-z0-9_]+$/i', $macroname)) {
$code = sprintf(
'%s%s($tpl, $ctx)',
$this->tag->generator->getFunctionPrefix(),
$macroname
);
$this->tag->generator->pushCode($code);
}
// external macro or ${macroname}, use PHPTAL at runtime to resolve it
else {
$code = $this->tag->generator->interpolateTalesVarsInString($this->expression);
$code = sprintf('<?php $tpl->executeMacro(%s); ?>', $code);
$this->tag->generator->pushHtml($code);
}
$this->popSlots();
}
public function end()
{
}
private function pushSlots()
{
// reset template slots on each macro call ?
//
// NOTE: defining a macro and using another macro on the same tag
// means inheriting from the used macro, thus slots are shared, it
// is a little tricky to understand but very natural to use.
//
// For example, we may have a main design.html containing our main
// website presentation with some slots (menu, content, etc...) then
// we may define a member.html macro which use the design.html macro
// for the general layout, fill the menu slot and let caller templates
// fill the parent content slot without interfering.
if (!$this->tag->hasAttribute('metal:define-macro')){
$this->tag->generator->pushCode('$ctx->pushSlots()');
}
}
private function popSlots()
{
// restore slots if not inherited macro
if (!$this->tag->hasAttribute('metal:define-macro')){
$this->tag->generator->pushCode('$ctx->popSlots()');
}
}
private function generateFillSlots($tag)
{
if (false == ($tag instanceOf PHPTAL_Php_Tree))
return;
// if the tag contains one of the allowed attribute, we generate it
foreach (self::$ALLOWED_ATTRIBUTES as $attribute){
if ($tag->hasAttribute($attribute)){
$tag->generate();
return;
}
}
// recurse
foreach ($tag->children as $child){
$this->generateFillSlots($child);
}
}
}
?>

View file

@ -0,0 +1,92 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Kornel Lesiński <kornel@aardvarkmedia.co.uk>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/** phptal:cache (note that's not tal:cache) caches element's HTML for a given time. Time is a number with 'd', 'h', 'm' or 's' suffix.
There's optional parameter that defines how cache should be shared. By default cache is not sensitive to template's context at all
- it's shared between all pages that use that template.
You can add per url to have separate copy of given element for every URL.
You can add per expression to have different cache copy for every different value of an expression (which MUST evaluate to a string).
Expression cannot refer to variables defined using tal:define on the same element.
NB:
* phptal:cache blocks can be nested, but outmost block will cache other blocks regardless of their freshness.
* you cannot use metal:fill-slot inside elements with phptal:cache
Examples:
<div phptal:cache="3h">...</div> <!-- <div> to be evaluated at most once per 3 hours. -->
<ul phptal:cache="1d per object/id">...</ul> <!-- <ul> be cached for one day, separately for each object. -->
*/
class PHPTAL_Php_Attribute_PHPTAL_Cache extends PHPTAL_Php_Attribute
{
private $cache_tag;
public function start()
{
if (!preg_match('/^\s*([0-9]+\s*|[a-zA-Z][a-zA-Z0-9_]*\s+)([dhms])\s*(?:\;?\s*per\s+([^;]+)|)\s*$/',$this->expression, $matches))
throw new PHPTAL_Exception("Cache attribute syntax error: ".$this->expression);
$cache_len = $matches[1];
if (!is_numeric($cache_len)) $cache_len = '$ctx->'.$cache_len;
switch($matches[2])
{
case 'd': $cache_len .= '*24'; /* no break */
case 'h': $cache_len .= '*60'; /* no break */
case 'm': $cache_len .= '*60'; /* no break */
}
$this->cache_tag = '"'.addslashes( $this->tag->node->getName() . ':' . $this->tag->node->getSourceLine()).'"';
$cache_per_expression = isset($matches[3])?trim($matches[3]):NULL;
if ($cache_per_expression == 'url')
{
$this->cache_tag .= '.$_SERVER["REQUEST_URI"]';
}
else if ($cache_per_expression == 'nothing') { }
else if ($cache_per_expression)
{
$code = $this->tag->generator->evaluateExpression($cache_per_expression);
if (is_array($code)) { throw new PHPTAL_Exception("Chained expressions in per-cache directive are not supported"); }
$old_cache_tag = $this->cache_tag;
$this->cache_tag = '$ctx->cache_tag_';
$this->tag->generator->doSetVar($this->cache_tag, '('.$code.')."@".' . $old_cache_tag );
}
$cond = '!file_exists(__FILE__.md5('.$this->cache_tag.')) || time() - '.$cache_len.' >= @filemtime(__FILE__.md5('.$this->cache_tag.'))';
$this->tag->generator->doIf($cond);
$this->tag->generator->doEval('ob_start()');
}
public function end()
{
$this->tag->generator->doEval('file_put_contents(__FILE__.md5('.$this->cache_tag.'), ob_get_flush())');
$this->tag->generator->doElse();
$this->tag->generator->doEval('readfile(__FILE__.md5('.$this->cache_tag.'))');
$this->tag->generator->doEnd();
}
}

View file

@ -0,0 +1,44 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* @package phptal.php.attribute.phptal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_PHPTAL_DEBUG extends PHPTAL_Php_Attribute
{
public function start()
{
$this->_oldMode = $this->tag->generator->setDebug(true);
}
public function end()
{
$this->tag->generator->setDebug( $this->_oldMode );
}
private $_oldMode;
}
?>

View file

@ -0,0 +1,66 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* @package phptal.php.attribute.phptal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_PHPTAL_ID extends PHPTAL_Php_Attribute
{
private $id;
public function start()
{
$this->id = str_replace('"', '\\\"', $this->expression);
// retrieve trigger
$this->tag->generator->doSetVar(
'$trigger',
'$tpl->getTrigger("'.$this->id.'")'
);
// if trigger found and trigger tells to proceed, we execute
// the node content
$cond = '$trigger && '
. '$trigger->start("%s", $tpl) == PHPTAL_Trigger::PROCEED';
$cond = sprintf($cond, $this->id);
$this->tag->generator->doIf($cond);
}
public function end()
{
// end of if PROCEED
$this->tag->generator->doEnd();
// if trigger found, notify the end of the node
$this->tag->generator->doIf('$trigger');
$this->tag->generator->pushCode(
'$trigger->end("'.$this->id.'", $tpl)'
);
$this->tag->generator->doEnd();
}
}
?>

View file

@ -0,0 +1,60 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* @package phptal.php.attribute.phptal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_PHPTAL_TALES extends PHPTAL_Php_Attribute
{
public function start()
{
$mode = trim($this->expression);
$mode = strtolower($mode);
if ($mode == '' || $mode == 'default')
$mode = 'tales';
if ($mode != 'php' && $mode != 'tales') {
$err = "Unsupported TALES mode '%s'";
$err = sprintf($err, $mode);
throw new PHPTAL_Exception(
$err,
$this->tag->getSourceFile(),
$this->tag->getSourceLine()
);
}
$this->_oldMode = $this->tag->generator->setTalesMode( $mode );
}
public function end()
{
$this->tag->generator->setTalesMode( $this->_oldMode );
}
private $_oldMode;
}
?>

View file

@ -0,0 +1,168 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= attribute_statement [';' attribute_statement]*
// attribute_statement ::= attribute_name expression
// attribute_name ::= [namespace ':'] Name
// namespace ::= Name
//
// examples:
//
// <a href="/sample/link.html"
// tal:attributes="href here/sub/absolute_url">
// <textarea rows="80" cols="20"
// tal:attributes="rows request/rows;cols request/cols">
//
// IN PHPTAL: attributes will not work on structured replace.
//
require_once PHPTAL_DIR.'PHPTAL/Php/TalesChainExecutor.php';
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Attributes
extends PHPTAL_Php_Attribute
implements PHPTAL_Php_TalesChainReader
{
const ATT_FULL_REPLACE = '$__ATT_';
const ATT_VALUE_REPLACE = '$__att_';
// this regex is used to determine if an attribute is entirely replaced
// by a php variable or if only its value is replaced.
const REGEX_FULL_REPLACE = '/<?php echo \$__ATT_.*? ?>/';
public function start()
{
// split attributes using ; delimiter
$attrs = $this->tag->generator->splitExpression($this->expression);
foreach ($attrs as $exp) {
list($attribute, $expression) = $this->parseSetExpression($exp);
if ($expression) {
$this->prepareAttribute($attribute, $expression);
}
}
}
public function end()
{
}
private function prepareAttribute($attribute, $expression)
{
$code = $this->extractEchoType(trim($expression));
$code = $this->tag->generator->evaluateExpression($code);
// if $code is an array then the attribute value is decided by a
// tales chained expression
if (is_array($code)) {
return $this->prepareChainedAttribute2($attribute, $code);
}
// XHTML boolean attribute does not appear when empty of false
if (PHPTAL_Dom_Defs::getInstance()->isBooleanAttribute($attribute)) {
return $this->prepareBooleanAttribute($attribute, $code);
}
// regular attribute which value is the evaluation of $code
$attkey = self::ATT_VALUE_REPLACE . $this->getVarName($attribute);
if ($this->_echoType == PHPTAL_Php_Attribute::ECHO_STRUCTURE)
$value = $code;
else
$value = $this->tag->generator->escapeCode($code);
$this->tag->generator->doSetVar($attkey, $value);
$this->tag->overwriteAttributeWithPhpValue($attribute, $attkey);
}
private function prepareChainedAttribute2($attribute, $chain)
{
$this->_default = false;
$this->_attribute = $attribute;
if (array_key_exists($attribute, $this->tag->attributes)) {
$this->_default = $this->tag->attributes[$attribute];
}
$this->_attkey = self::ATT_FULL_REPLACE.$this->getVarName($attribute);
$executor = new PHPTAL_Php_TalesChainExecutor($this->tag->generator, $chain, $this);
$this->tag->overwriteAttributeWithPhpValue($attribute, $this->_attkey);
}
private function prepareBooleanAttribute($attribute, $code)
{
$attkey = self::ATT_FULL_REPLACE.$this->getVarName($attribute);
$value = "' $attribute=\"$attribute\"'";
$this->tag->generator->doIf($code);
$this->tag->generator->doSetVar($attkey, $value);
$this->tag->generator->doElse();
$this->tag->generator->doSetVar($attkey, '\'\'');
$this->tag->generator->doEnd();
$this->tag->overwriteAttributeWithPhpValue($attribute, $attkey);
}
private function getVarName($attribute)
{
return strtr($attribute,':-', '__');
}
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$this->tag->generator->doSetVar(
$this->_attkey,
"''"
);
$executor->breakChain();
}
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$code = ($this->_default !== false)
? "' $this->_attribute=\"".str_replace("'",'\\\'',$this->_default)."\"'" // default value
: '\'\''; // do not print attribute
$this->tag->generator->doSetVar($this->_attkey, $code);
$executor->breakChain();
}
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast)
{
if (!$islast) {
$condition = "!phptal_isempty($this->_attkey = $exp)";
}
else {
$condition = "NULL !== ($this->_attkey = $exp)";
}
$executor->doIf($condition);
if ($this->_echoType == PHPTAL_Php_Attribute::ECHO_STRUCTURE)
$value = $this->_attkey;
else
$value = $this->tag->generator->escapeCode($this->_attkey);
$this->tag->generator->doSetVar($this->_attkey, "' $this->_attribute=\"'.$value.'\"'");
}
}
?>

View file

@ -0,0 +1,40 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* @package phptal.php.attribute.tal
*/
class PHPTAL_Php_Attribute_TAL_Comment extends PHPTAL_Php_Attribute
{
public function start()
{
$this->tag->generator->doComment($this->expression);
}
public function end()
{
}
}
?>

View file

@ -0,0 +1,101 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
// TAL Specifications 1.4
//
// argument ::= expression
//
// Example:
//
// <p tal:condition="here/copyright"
// tal:content="here/copyright">(c) 2000</p>
//
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
require_once PHPTAL_DIR.'PHPTAL/Php/TalesChainExecutor.php';
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Condition
extends PHPTAL_Php_Attribute
implements PHPTAL_Php_TalesChainReader
{
private $expressions = array();
public function start()
{
$code = $this->tag->generator->evaluateExpression($this->expression);
// If it's a chained expression build a new code path
if (is_array($code)) {
$this->expressions = array();
$executor = new PHPTAL_Php_TalesChainExecutor( $this->tag->generator, $code, $this );
return;
}
// Force a falsy condition if the nothing keyword is active
if ($code == PHPTAL_TALES_NOTHING_KEYWORD) {
$code = 'false';
}
$this->tag->generator->doIf($code);
}
public function end()
{
$this->tag->generator->doEnd();
}
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast)
{
// check if the expression is empty
if ( $exp !== 'false' ) {
$this->expressions[] = '!phptal_isempty($__content__ = ' . $exp . ')';
}
if ( $islast ) {
// for the last one in the chain build a ORed condition
$this->tag->generator->doIf( implode(' || ', $this->expressions ) );
// The executor will always end an if so we output a dummy if
$executor->doIf('false');
}
}
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
// end the chain
$this->talesChainPart( $executor, 'false', true );
$executor->breakChain();
}
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
throw new PHPTAL_Exception('\'default\' keyword not allowed on condition expressions');
}
}

View file

@ -0,0 +1,98 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= (['text'] | 'structure') expression
//
// Example:
//
// <p tal:content="user/name">Fred Farkas</p>
//
//
require_once PHPTAL_DIR.'PHPTAL/Php/TalesChainExecutor.php';
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Content
extends PHPTAL_Php_Attribute
implements PHPTAL_Php_TalesChainReader
{
public function start()
{
$expression = $this->extractEchoType($this->expression);
$code = $this->tag->generator->evaluateExpression($expression);
if (is_array($code)) {
return $this->generateChainedContent($code);
}
if ($code == PHPTAL_TALES_NOTHING_KEYWORD) {
return;
}
if ($code == PHPTAL_TALES_DEFAULT_KEYWORD) {
return $this->generateDefault();
}
$this->doEcho($code);
}
public function end()
{
}
private function generateDefault()
{
$this->tag->generateContent(true);
}
private function generateChainedContent($code)
{
$executor = new PHPTAL_Php_TalesChainExecutor($this->tag->generator, $code, $this);
}
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast)
{
$executor->doIf('!phptal_isempty($__content__ = '.$exp.')');
$this->doEcho('$__content__');
}
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->breakChain();
}
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$this->generateDefault();
$executor->breakChain();
}
}
?>

View file

@ -0,0 +1,183 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL spec 1.4 for tal:define content
//
// argument ::= define_scope [';' define_scope]*
// define_scope ::= (['local'] | 'global') define_var
// define_var ::= variable_name expression
// variable_name ::= Name
//
// Note: If you want to include a semi-colon (;) in an expression, it must be escaped by doubling it (;;).*
//
// examples:
//
// tal:define="mytitle template/title; tlen python:len(mytitle)"
// tal:define="global company_name string:Digital Creations, Inc."
//
require_once PHPTAL_DIR.'PHPTAL/Php/TalesChainExecutor.php';
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Define
extends PHPTAL_Php_Attribute
implements PHPTAL_Php_TalesChainReader
{
public function start()
{
$expressions = $this->tag->generator->splitExpression($this->expression);
$definesAnyNonGlobalVars = false;
foreach ($expressions as $exp){
list($defineScope, $defineVar, $expression) = $this->parseExpression($exp);
if (!$defineVar) {
continue;
}
$this->_defineScope = $defineScope;
if ($defineScope != 'global') $definesAnyNonGlobalVars = true; // <span tal:define="global foo" /> should be invisible, but <img tal:define="bar baz" /> not
if ($this->_defineScope != 'global' && !$this->_pushedContext){
$this->tag->generator->pushContext();
$this->_pushedContext = true;
}
$this->_defineVar = $defineVar;
if ($expression === null) {
// no expression give, use content of tag as value for newly defined
// var.
$this->bufferizeContent();
continue;
}
$code = $this->tag->generator->evaluateExpression($expression);
if (is_array($code)){
$this->chainedDefine($code);
}
elseif ($code == PHPTAL_TALES_NOTHING_KEYWORD) {
$this->doDefineVarWith('null');
}
else {
$this->doDefineVarWith($code);
}
}
// if the content of the tag was buffered or the tag has nothing to tell, we hide it.
if ($this->_buffered || (!$definesAnyNonGlobalVars && !$this->tag->hasRealContent() && !$this->tag->hasRealAttributes())){
$this->tag->hidden = true;
}
}
public function end()
{
if ($this->_pushedContext){
$this->tag->generator->popContext();
}
}
private function chainedDefine($parts)
{
$executor = new PHPTAL_Php_TalesChainExecutor(
$this->tag->generator, $parts, $this
);
}
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$this->doDefineVarWith('null');
$executor->breakChain();
}
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$this->bufferizeContent();
$executor->breakChain();
}
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast)
{
if ($this->_defineScope == 'global'){
$executor->doIf('($glb->'.$this->_defineVar.' = '.$exp.') !== null');
}
else {
$executor->doIf('($ctx->'.$this->_defineVar.' = '.$exp.') !== null');
}
}
/**
* Parse the define expression, already splitted in sub parts by ';'.
*/
public function parseExpression($exp)
{
$defineScope = false; // (local | global)
$defineVar = false; // var to define
// extract defineScope from expression
$exp = trim($exp);
if (preg_match('/^(local|global)\s+(.*?)$/ism', $exp, $m)) {
list(,$defineScope, $exp) = $m;
$exp = trim($exp);
}
// extract varname and expression from remaining of expression
list($defineVar, $exp) = $this->parseSetExpression($exp);
if ($exp !== null) $exp = trim($exp);
return array($defineScope, $defineVar, $exp);
}
private function bufferizeContent()
{
if (!$this->_buffered){
$this->tag->generator->pushCode( 'ob_start()' );
$this->tag->generateContent();
$this->tag->generator->pushCode('$__tmp_content__ = ob_get_clean()');
$this->_buffered = true;
}
$this->doDefineVarWith('$__tmp_content__');
}
private function doDefineVarWith($code)
{
if ($this->_defineScope == 'global'){
$this->tag->generator->doSetVar('$glb->'.$this->_defineVar, $code);
}
else {
$this->tag->generator->doSetVar('$ctx->'.$this->_defineVar, $code);
}
}
private $_buffered = false;
private $_defineScope = null;
private $_defineVar = null;
private $_pushedContext = false;
}
?>

View file

@ -0,0 +1,74 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= [expression]
//
// Example:
//
// <div tal:omit-tag="" comment="This tag will be removed">
// <i>...but this text will remain.</i>
// </div>
//
// <b tal:omit-tag="not:bold">I may not be bold.</b>
//
// To leave the contents of a tag in place while omitting the surrounding
// start and end tag, use the omit-tag statement.
//
// If its expression evaluates to a false value, then normal processing
// of the element continues.
//
// If the expression evaluates to a true value, or there is no
// expression, the statement tag is replaced with its contents. It is up to
// the interface between TAL and the expression engine to determine the
// value of true and false. For these purposes, the value nothing is false,
// and cancellation of the action has the same effect as returning a
// false value.
//
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_OmitTag extends PHPTAL_Php_Attribute
{
public function start()
{
if (trim($this->expression) == ''){
$this->tag->headFootDisabled = true;
}
else {
// print tag header/foot only if condition is false
$cond = $this->tag->generator->evaluateExpression($this->expression);
$this->tag->headFootPrintCondition = '!('.$cond.')';
}
}
public function end()
{
}
}
?>

View file

@ -0,0 +1,81 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= (['text'] | 'structure') expression
//
// Example:
//
// <p tal:on-error="string: Error! This paragraph is buggy!">
// My name is <span tal:replace="here/SlimShady" />.<br />
// (My login name is
// <b tal:on-error="string: Username is not defined!"
// tal:content="user">Unknown</b>)
// </p>
//
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_OnError extends PHPTAL_Php_Attribute
{
const ERR_VAR = '$__err__';
public function start()
{
$this->tag->generator->doTry();
$this->tag->generator->pushCode('ob_start()');
}
public function end()
{
$this->tag->generator->pushCode('ob_end_flush()');
$this->tag->generator->doCatch('Exception '.self::ERR_VAR);
$this->tag->generator->pushCode('$tpl->addError('.self::ERR_VAR.')');
$this->tag->generator->pushCode('ob_end_clean()');
$expression = $this->extractEchoType($this->expression);
$code = $this->tag->generator->evaluateExpression($expression);
switch ($code) {
case PHPTAL_TALES_NOTHING_KEYWORD:
break;
case PHPTAL_TALES_DEFAULT_KEYWORD:
$this->tag->generator->pushHtml('<pre class="phptalError"');
$this->tag->generator->doEcho(self::ERR_VAR);
$this->tag->generator->pushHtml('</pre>');
break;
default:
$this->doEcho($code);
break;
}
$this->tag->generator->doEnd();
}
}
?>

View file

@ -0,0 +1,115 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= variable_name expression
// variable_name ::= Name
//
// Example:
//
// <p tal:repeat="txt python:'one', 'two', 'three'">
// <span tal:replace="txt" />
// </p>
// <table>
// <tr tal:repeat="item here/cart">
// <td tal:content="repeat/item/index">1</td>
// <td tal:content="item/description">Widget</td>
// <td tal:content="item/price">$1.50</td>
// </tr>
// </table>
//
// The following information is available from an Iterator:
//
// * index - repetition number, starting from zero.
// * number - repetition number, starting from one.
// * even - true for even-indexed repetitions (0, 2, 4, ...).
// * odd - true for odd-indexed repetitions (1, 3, 5, ...).
// * start - true for the starting repetition (index 0).
// * end - true for the ending, or final, repetition.
// * length - length of the sequence, which will be the total number of repetitions.
//
// * letter - count reps with lower-case letters: "a" - "z", "aa" - "az", "ba" - "bz", ..., "za" - "zz", "aaa" - "aaz", and so forth.
// * Letter - upper-case version of letter.
// * roman - count reps with lower-case roman numerals: "i", "ii", "iii", "iv", "v", "vi" ...
// * Roman - upper-case version of roman numerals.
///
// * first - true for the first item in a group - see note below
// * lasst - true for the last item in a group - see note below
//
// Note: first and last are intended for use with sorted sequences. They try to
// divide the sequence into group of items with the same value. If you provide
// a path, then the value obtained by following that path from a sequence item
// is used for grouping, otherwise the value of the item is used. You can
// provide the path by appending it to the path from the repeat variable,
// as in "repeat/item/first/color".
//
// PHPTAL: index, number, even, etc... will be stored in the
// $ctx->repeat->'item' object. Thus $ctx->repeat->item->odd
//
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Repeat extends PHPTAL_Php_Attribute
{
const REPEAT = '$__repeat__';
public function start()
{
// alias to repeats handler to avoid calling extra getters on each variable access
$this->tag->generator->doSetVar( self::REPEAT, '$ctx->repeat' );
list( $varName, $expression ) = $this->parseSetExpression( $this->expression );
$code = $this->tag->generator->evaluateExpression( $expression );
$item = '$ctx->' . $varName;
$controller = self::REPEAT . '->' . $varName;
// reset item var into template context
/* // Is this actually needed?
$this->tag->generator->doIf( '!isset('.$this->item.')' );
$this->tag->generator->doSetVar( $this->item, 'false' );
$this->tag->generator->doEnd();
*/
// instantiate controller using expression
$this->tag->generator->doSetVar( $controller, 'new PHPTAL_RepeatController('.$code.')' );
// Lets loop the iterator with a foreach construct
$this->tag->generator->doForeach( $item, $controller );
}
public function end()
{
$this->tag->generator->doEnd();
}
private $item;
private $controller;
}
?>

View file

@ -0,0 +1,117 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= (['text'] | 'structure') expression
//
// Default behaviour : text
//
// <span tal:replace="template/title">Title</span>
// <span tal:replace="text template/title">Title</span>
// <span tal:replace="structure table" />
// <span tal:replace="nothing">This element is a comment.</span>
//
require_once PHPTAL_DIR.'PHPTAL/Php/TalesChainExecutor.php';
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Replace
extends PHPTAL_Php_Attribute
implements PHPTAL_Php_TalesChainReader
{
const REPLACE_VAR = '$__replace__';
public function start()
{
// tal:replace="" => do nothing and ignore node
if (trim($this->expression) == ""){
return;
}
$expression = $this->extractEchoType($this->expression);
$code = $this->tag->generator->evaluateExpression($expression);
// chained expression
if (is_array($code)){
return $this->replaceByChainedExpression($code);
}
// nothing do nothing
if ($code == PHPTAL_TALES_NOTHING_KEYWORD) {
return;
}
// default generate default tag content
if ($code == PHPTAL_TALES_DEFAULT_KEYWORD) {
return $this->generateDefault();
}
// replace tag with result of expression
$this->doEcho($code);
}
public function end()
{
}
private function replaceByChainedExpression($expArray)
{
$executor = new PHPTAL_Php_TalesChainExecutor(
$this->tag->generator, $expArray, $this
);
}
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->continueChain();
}
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$this->generateDefault();
$executor->breakChain();
}
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast)
{
$executor->doIf('!phptal_isempty('.self::REPLACE_VAR.' = '.$exp.')');
$this->doEcho(self::REPLACE_VAR);
}
private function generateDefault()
{
$this->tag->generateSurroundHead();
$this->tag->generateHead();
$this->tag->generateContent();
$this->tag->generateFoot();
$this->tag->generateSurroundFoot();
}
}
?>

View file

@ -0,0 +1,55 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Node.php';
require_once PHPTAL_DIR.'PHPTAL/Php/State.php';
require_once PHPTAL_DIR.'PHPTAL/Php/CodeWriter.php';
/**
* @package phptal.php
*/
class PHPTAL_Php_CodeGenerator
{
public function __construct($function_name, $source_path)
{
$this->_functionName = $function_name;
$this->_sourceFile = $source_path;
$this->_state = new PHPTAL_Php_State();
$this->_writer = new PHPTAL_Php_CodeWriter($this->_state);
}
public function setOutputMode($mode)
{
$this->_state->setOutputMode($mode);
}
public function setEncoding($enc)
{
$this->_state->setEncoding($enc);
}
public function generate(PHPTAL_Dom_Tree $tree)
{
$treeGen = new PHPTAL_Php_Tree($this->_writer, $tree);
$this->_writer->doComment('Generated by PHPTAL from '.$this->_sourceFile);
$this->_writer->doFunction($this->_functionName, '$tpl, $ctx');
$this->_writer->setFunctionPrefix($this->_functionName . "_");
$this->_writer->doSetVar('$glb', '$tpl->getGlobalContext()');
$this->_writer->doSetVar('$_translator', '$tpl->getTranslator()');
$treeGen->generate();
$this->_writer->doEnd();
}
public function getResult()
{
return $this->_writer->getResult();
}
private $_functionName;
private $_sourceFile;
private $_writer;
private $_state;
}
?>

394
PHPTAL/Php/CodeWriter.php Normal file
View file

@ -0,0 +1,394 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* Helps generate php representation of a template.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_CodeWriter
{
public $_indentation = 0;
public function __construct(PHPTAL_Php_State $state)
{
$this->_state = $state;
}
public function getResult()
{
$this->flush();
$this->_result = trim($this->_result);
return $this->_result;
}
public function setDocType(PHPTAL_Php_Doctype $dt)
{
$this->_doctype = str_replace('\'', '\\\'', $dt->node->getValue());
}
public function setXmlDeclaration(PHPTAL_Php_XmlDeclaration $dt)
{
$this->_xmldeclaration = str_replace('\'', '\\\'', $dt->node->getValue());
}
public function setFunctionPrefix($prefix)
{
$this->_functionPrefix = $prefix;
}
public function getFunctionPrefix()
{
return $this->_functionPrefix;
}
/**
* Returns old tales mode.
*/
public function setTalesMode($mode)
{
return $this->_state->setTalesMode($mode);
}
public function splitExpression($src)
{
preg_match_all('/(?:[^;]+|;;)+/sm', $src, $array);
$array = $array[0];
foreach($array as &$a) $a = str_replace(';;',';',$a);
return $array;
}
public function evaluateExpression($src)
{
return $this->_state->evalTalesExpression($src);
}
public function indent()
{
$this->_indentation ++;
}
public function unindent()
{
$this->_indentation --;
}
public function flush()
{
$this->flushCode();
$this->flushHtml();
}
public function noThrow($bool)
{
if ($bool){
$this->pushCode('$ctx->noThrow(true)');
}
else {
$this->pushCode('$ctx->noThrow(false)');
}
}
public function flushCode()
{
if (count($this->_codeBuffer) == 0)
return;
// special treatment for one code line
if (count($this->_codeBuffer) == 1){
$codeLine = $this->_codeBuffer[0];
// avoid adding ; after } and {
if (!preg_match('/\}|\{\s+$/', $codeLine))
$this->_result .= '<?php '.$codeLine.'; ?>';
else
$this->_result .= '<?php '.$codeLine.' ?>';
$this->_codeBuffer = array();
return;
}
$this->_result .= '<?php '."\n";
foreach ($this->_codeBuffer as $codeLine) {
// avoid adding ; after } and {
if (!preg_match('/\}|\{\s+$/', $codeLine))
$this->_result .= $codeLine . ' ;'."\n";
else
$this->_result .= $codeLine;
}
$this->_result .= '?>';
$this->_codeBuffer = array();
}
public function flushHtml()
{
if (count($this->_htmlBuffer) == 0) return;
$this->_result .= join( '', $this->_htmlBuffer );
$this->_htmlBuffer = array();
}
public function doDoctype()
{
if ($this->_doctype){
$code = '$ctx->setDocType(\''.$this->_doctype.'\')';
$this->pushCode($code);
}
}
public function doXmlDeclaration()
{
if ($this->_xmldeclaration){
$code = '$ctx->setXmlDeclaration(\''.$this->_xmldeclaration.'\')';
$this->pushCode($code);
}
}
public function doFunction($name, $params)
{
$name = $this->_functionPrefix . $name;
$this->pushGeneratorContext();
$this->pushCode("function $name( $params ) {\n");
$this->indent();
array_push($this->_segments, 'function');
}
public function doComment($comment)
{
$comment = str_replace('*/', '* /', $comment);
$this->pushCode("/* $comment */");
}
public function doEval($code)
{
$this->pushCode($code);
}
public function doForeach($out, $source)
{
array_push($this->_segments, 'foreach');
$this->pushCode("foreach ($source as \$__key__ => $out ):");
$this->indent();
}
public function doEnd()
{
$segment = array_pop($this->_segments);
$this->unindent();
if ($segment == 'function') {
$this->pushCode("\n}\n\n");
$functionCode = $this->getResult();
$this->popGeneratorContext();
$this->_result = $functionCode . $this->_result;
}
else if ($segment == 'try')
$this->pushCode('}');
else if ($segment == 'catch')
$this->pushCode('}');
else
$this->pushCode("end$segment");
}
public function doTry()
{
array_push($this->_segments, 'try');
$this->pushCode('try {');
$this->indent();
}
public function doSetVar($varname, $code)
{
$this->pushCode($varname.' = '.$code);
}
public function doCatch($catch)
{
$this->doEnd();
array_push($this->_segments, 'catch');
$code = 'catch(%s) {';
$this->pushCode(sprintf($code, $catch));
$this->indent();
}
public function doIf($condition)
{
array_push($this->_segments, 'if');
$this->pushCode('if ('.$condition.'): ');
$this->indent();
}
public function doElseIf($condition)
{
$this->unindent();
$this->pushCode('elseif ('.$condition.'): ');
$this->indent();
}
public function doElse()
{
$this->unindent();
$this->pushCode('else: ');
$this->indent();
}
public function doEcho($code)
{
$this->flush();
$html = '<?php echo %s ?>';
$html = sprintf($html, $this->escapeCode($code));
$this->pushHtml($html);
}
public function doEchoRaw($code)
{
$this->pushHtml('<?php echo '.$code.' ?>');
}
public function pushHtml($html)
{
$html = $this->_state->interpolateTalesVarsInHtml($html);
$this->flushCode();
array_push($this->_htmlBuffer, $html);
}
public function pushRawHtml($html)
{
$this->flushCode();
array_push($this->_htmlBuffer, $html);
}
public function pushString($str)
{
$this->flushCode();
// replace ${var} inside strings
while (preg_match('/^(.*?)((?<!\$)\$\{[^\}]*?\})(.*?)$/s', $str, $m)){
list(,$before,$expression,$after) = $m;
$before = $this->escapeLTandGT($before);
array_push($this->_htmlBuffer, $before);
$expression = $this->_state->interpolateTalesVarsInHtml($expression);
array_push($this->_htmlBuffer, $expression);
$str = $after;
}
$str = str_replace('$${', '${', $str);
if (strlen($str) > 0){
$str = $this->escapeLTandGT($str);
array_push($this->_htmlBuffer, $str);
}
}
public function pushCode($codeLine)
{
$this->flushHtml();
$codeLine = $this->indentSpaces() . $codeLine;
array_push($this->_codeBuffer, $codeLine);
}
public function escapeLTandGT($str){
$str = str_replace('<', '&lt;', $str);
$str = str_replace('>', '&gt;', $str);
return $str;
}
public function escapeCode($code)
{
return $this->_state->htmlchars($code);
}
public function getEncoding()
{
return $this->_state->getEncoding();
}
public function interpolateTalesVarsInString($src)
{
return $this->_state->interpolateTalesVarsInString($src);
}
public function setDebug($bool)
{
return $this->_state->setDebug($bool);
}
public function isDebugOn()
{
return $this->_state->isDebugOn();
}
public function getOutputMode()
{
return $this->_state->getOutputMode();
}
public function pushContext()
{
$this->pushCode('$ctx = $tpl->pushContext()');
}
public function popContext()
{
$this->pushCode('$ctx = $tpl->popContext()');
}
// ~~~~~ Private members ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private function indentSpaces()
{
return str_repeat("\t", $this->_indent);
}
private function pushGeneratorContext()
{
array_push($this->_contexts, clone $this);
$this->_result = "";
$this->_indent = 0;
$this->_codeBuffer = array();
$this->_htmlBuffer = array();
$this->_segments = array();
}
private function popGeneratorContext()
{
$oldContext = array_pop($this->_contexts);
$this->_result = $oldContext->_result;
$this->_indent = $oldContext->_indent;
$this->_codeBuffer = $oldContext->_codeBuffer;
$this->_htmlBuffer = $oldContext->_htmlBuffer;
$this->_segments = $oldContext->_segments;
}
private $_state;
private $_result = "";
private $_indent = 0;
private $_codeBuffer = array();
private $_htmlBuffer = array();
private $_segments = array();
private $_contexts = array();
private $_functionPrefix = "";
private $_doctype = "";
private $_xmldeclaration = "";
}
?>

View file

@ -0,0 +1,101 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* @package phptal.php
*/
class PHPTAL_Php_ElementWriter
{
public function __construct(PHPTAL_Php_CodeWriter $writer, PHPTAL_Php_Element $tag)
{
$this->_writer = $writer;
$this->_tag = $tag;
}
public function writeHead()
{
if ($this->_tag->headFootDisabled)
return;
if ($this->_tag->headFootPrintCondition){
$this->_writer->doIf($this->_tag->headFootPrintCondition);
}
$this->_writer->pushHtml('<'.$this->_tag->name);
$this->_writeAttributes();
if ($this->_tag->isEmptyNode()){
$this->_writer->pushHtml('/>');
}
else {
$this->_writer->pushHtml('>');
}
if ($this->_tag->headFootPrintCondition){
$this->_writer->doEnd();
}
}
public function writeFoot()
{
if ($this->_tag->headFootDisabled)
return;
if ($this->_tag->isEmptyNode())
return;
if ($this->_tag->headFootPrintCondition){
$this->_writer->doIf($this->_tag->headFootPrintCondition);
}
$this->_writer->pushHtml('</'.$this->_tag->name.'>');
if ($this->_tag->headFootPrintCondition){
$this->_writer->doEnd();
}
}
public function writeAttributes()
{
$fullreplaceRx = PHPTAL_Php_Attribute_TAL_Attributes::REGEX_FULL_REPLACE;
foreach ($this->_tag->attributes as $key=>$value) {
if (preg_match($fullreplaceRx, $value)){
$this->_writer->pushHtml($value);
}
/*
else if (strpos('<?php', $value) === 0){
$this->_writer->pushHtml(' '.$key.'="');
$this->_writer->pushRawHtml($value);
$this->_writer->pushHtml('"');
}
*/
else {
$this->_writer->pushHtml(' '.$key.'="'.$value.'"');
}
}
}
private $_tag;
private $_writer;
}
?>

507
PHPTAL/Php/Node.php Normal file
View file

@ -0,0 +1,507 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Dom/Defs.php';
require_once PHPTAL_DIR.'PHPTAL/Php/CodeWriter.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* Document node abstract class.
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
abstract class PHPTAL_Php_Node
{
public $node;
public $generator;
public function __construct(PHPTAL_Php_CodeWriter $generator, PHPTAL_Dom_Node $node)
{
$this->generator = $generator;
$this->node = $node;
}
public function getSourceFile()
{
return $this->node->getSourceFile();
}
public function getSourceLine()
{
return $this->node->getSourceLine();
}
public abstract function generate();
}
/**
* Node container.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Tree extends PHPTAL_Php_Node
{
public $children;
public function __construct(PHPTAL_Php_CodeWriter $gen, $node)
{
parent::__construct($gen,$node);
$this->children = array();
foreach ($node->getChildren() as $child){
if ($child instanceOf PHPTAL_Dom_Element){
$gen = new PHPTAL_Php_Element($this->generator, $child);
}
else if ($child instanceOf PHPTAL_Dom_Text){
$gen = new PHPTAL_Php_Text($this->generator, $child);
}
else if ($child instanceOf PHPTAL_Dom_Doctype){
$gen = new PHPTAL_Php_Doctype($this->generator, $child);
}
else if ($child instanceOf PHPTAL_Dom_XmlDeclaration){
$gen = new PHPTAL_Php_XmlDeclaration($this->generator, $child);
}
else if ($child instanceOf PHPTAL_Dom_Specific){
$gen = new PHPTAL_Php_Specific($this->generator, $child);
}
else if ($child instanceOf PHPTAL_Dom_Comment){
$gen = new PHPTAL_Php_Comment($this->generator, $child);
}
else {
throw new PHPTAL_Exception('Unhandled node class '.get_class($child));
}
array_push($this->children, $gen);
}
}
public function generate()
{
try
{
foreach ($this->children as $child){
$child->generate();
}
}
catch(PHPTAL_Exception $e)
{
$e->hintSrcPosition($this->getSourceFile(), $this->getSourceLine());
throw $e;
}
}
}
/**
* Document Tag representation.
*
* This is the main class used by PHPTAL because TAL is a Template Attribute
* Language, other Node kinds are (usefull) toys.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Element extends PHPTAL_Php_Tree
{
const ERR_ATTRIBUTES_CONFLICT =
"Attribute conflict in '%s' at line '%d', '%s' cannot appear with '%s'";
public $name;
public $attributes = array();
public $talAttributes = array();
public $overwrittenAttributes = array();
public $replaceAttributes = array();
public $contentAttributes = array();
public $surroundAttributes = array();
public $headFootDisabled = false;
public $headFootPrintCondition = false;
public $hidden = false;
public function __construct(PHPTAL_Php_CodeWriter $generator, $node)
{
parent::__construct($generator, $node);
$this->name = $node->getName();
$this->attributes = $node->attributes;
$this->xmlns = $node->getXmlnsState();
$this->prepare();
}
private function prepare()
{
$this->prepareAttributes();
$this->separateAttributes();
$this->orderTalAttributes();
}
public function generate()
{
if ($this->generator->isDebugOn()){
$this->generator->pushCode('$ctx->__line = '.$this->getSourceLine());
$this->generator->doComment('tag "'.$this->name.'" from line '.$this->getSourceLine());
}
if (count($this->replaceAttributes) > 0) {
$this->generateSurroundHead();
foreach ($this->replaceAttributes as $att) {
$att->start();
$att->end();
}
$this->generateSurroundFoot();
return;
}
$this->generateSurroundHead();
// a surround tag may decide to hide us (tal:define for example)
if (!$this->hidden){
$this->generateHead();
$this->generateContent();
$this->generateFoot();
}
$this->generateSurroundFoot();
}
/** Returns true if the element contains specified PHPTAL attribute. */
public function hasAttribute($name)
{
return $this->node->hasAttribute($name);
}
/** Returns the value of specified PHPTAL attribute. */
public function getAttribute($name)
{
return $this->node->getAttribute($name);
}
public function isOverwrittenAttribute($name)
{
return array_key_exists($name, $this->overwrittenAttributes);
}
public function getOverwrittenAttributeVarName($name)
{
return $this->overwrittenAttributes[$name];
}
public function overwriteAttributeWithPhpValue($name, $phpVariable)
{
$this->attributes[$name] = '<?php echo '.$phpVariable.' ?>';
$this->overwrittenAttributes[$name] = $phpVariable;
}
/**
* Returns true if this element or one of its PHPTAL attributes has some
* content to print (an empty text node child does not count).
*/
public function hasRealContent()
{
return $this->node->hasRealContent()
|| count($this->contentAttributes) > 0;
}
public function hasRealAttributes()
{
return ((count($this->attributes) - count($this->talAttributes)) > 0) || $this->hasAttribute('tal:attributes');
}
// ~~~~~ Generation methods may be called by some PHPTAL attributes ~~~~~
public function generateSurroundHead()
{
foreach ($this->surroundAttributes as $att) {
$att->start();
}
}
public function generateHead()
{
if ($this->headFootDisabled) return;
if ($this->headFootPrintCondition) {
$this->generator->doIf($this->headFootPrintCondition);
}
$this->generator->pushHtml('<'.$this->name);
$this->generateAttributes();
if ($this->isEmptyNode()){
$this->generator->pushHtml('/>');
}
else {
$this->generator->pushHtml('>');
}
if ($this->headFootPrintCondition) {
$this->generator->doEnd();
}
}
public function generateContent($realContent=false)
{
if ($this->isEmptyNode()){
return;
}
if (!$realContent && count($this->contentAttributes) > 0) {
foreach ($this->contentAttributes as $att) {
$att->start();
$att->end();
}
return;
}
parent::generate();
}
public function generateFoot()
{
if ($this->headFootDisabled)
return;
if ($this->isEmptyNode())
return;
if ($this->headFootPrintCondition) {
$this->generator->doIf($this->headFootPrintCondition);
}
$this->generator->pushHtml( '</'.$this->name.'>' );
if ($this->headFootPrintCondition) {
$this->generator->doEnd();
}
}
public function generateSurroundFoot()
{
for ($i = (count($this->surroundAttributes)-1); $i >= 0; $i--) {
$this->surroundAttributes[$i]->end();
}
}
// ~~~~~ Private members ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private function generateAttributes()
{
// A phptal attribute can modify any node attribute replacing
// its value by a <?php echo $somevalue ?\ >.
//
// The entire attribute (key="value") can be replaced using the
// '$__ATT_' value code, it is very usefull for xhtml boolean
// attributes like selected, checked, etc...
//
// example:
//
// $tag->generator->pushCode(
// '$__ATT_checked = $somecondition ? \'checked="checked"\' : \'\''
// );
// $tag->attributes['checked'] = '<?php echo $__ATT_checked ?\>';
//
$fullreplaceRx = PHPTAL_Php_Attribute_TAL_Attributes::REGEX_FULL_REPLACE;
foreach ($this->attributes as $key=>$value) {
if (preg_match($fullreplaceRx, $value)){
$this->generator->pushHtml($value);
}
else if (strpos($value,'<?php') === 0){
$this->generator->pushHtml(' '.$key.'="');
$this->generator->pushRawHtml($value);
$this->generator->pushHtml('"');
}
else {
$this->generator->pushHtml(' '.$key.'="'.$value.'"');
}
}
}
private function getNodePrefix()
{
$result = false;
if (preg_match('/^(.*?):block$/', $this->name, $m)){
list(,$result) = $m;
}
return $result;
}
private function isEmptyNode()
{
return ($this->generator->getOutputMode() == PHPTAL::XHTML && PHPTAL_Dom_Defs::getInstance()->isEmptyTag($this->name)) ||
($this->generator->getOutputMode() == PHPTAL::XML && !$this->hasContent());
}
private function hasContent()
{
return count($this->children) > 0 || count($this->contentAttributes) > 0;
}
private function prepareAttributes()
{
//TODO: use registered namespaces instead of the raw list
if (preg_match('/^(tal|metal|phptal|i18n):block$/', $this->name, $m)) {
$this->headFootDisabled = true;
list(,$ns) = $m;
$attributes = array();
foreach ($this->attributes as $key=>$value) {
if ($this->xmlns->isPhpTalAttribute("$ns:$key")) {
$attributes["$ns:$key"] = $value;
}
else {
$attributes[$key] = $value;
}
}
$this->attributes = $attributes;
}
}
private function separateAttributes()
{
$attributes = array();
$this->talAttributes = array();
foreach ($this->attributes as $key=>$value) {
// remove handled xml namespaces
if (PHPTAL_Dom_Defs::getInstance()->isHandledXmlNs($key,$value)){
}
else if ($this->xmlns->isPhpTalAttribute($key)) {
$this->talAttributes[$key] = $value;
}
else if (PHPTAL_Dom_Defs::getInstance()->isBooleanAttribute($key)) {
$attributes[$key] = $key;
}
else {
$attributes[$key] = $value;
}
}
$this->attributes = $attributes;
}
private function orderTalAttributes()
{
$attributes = array();
foreach ($this->talAttributes as $key=>$exp){
$name = $this->xmlns->unAliasAttribute($key);
$att = PHPTAL_Dom_Defs::getInstance()->getNamespaceAttribute($name);
if (array_key_exists($att->getPriority(), $attributes)){
$err = sprintf(self::ERR_ATTRIBUTES_CONFLICT,
$this->name,
$this->getSourceLine(),
$key,
$attributes[$att->getPriority()][0]
);
throw new PHPTAL_Exception($err);
}
$attributes[$att->getPriority()] = array($key, $att, $exp);
}
ksort($attributes);
$this->talHandlers = array();
foreach ($attributes as $prio => $dat){
list($key, $att, $exp) = $dat;
$handler = $att->createAttributeHandler($this, $exp);
$this->talHandlers[$prio] = $handler;
if ($att instanceOf PHPTAL_NamespaceAttributeSurround)
$this->surroundAttributes[] = $handler;
else if ($att instanceOf PHPTAL_NamespaceAttributeReplace)
$this->replaceAttributes[] = $handler;
else if ($att instanceOf PHPTAL_NamespaceAttributeContent)
$this->contentAttributes[] = $handler;
else
throw new PHPTAL_Exception("Unknown namespace attribute class ".get_class($att));
}
}
}
/**
* @package phptal.php
*/
class PHPTAL_Php_Comment extends PHPTAL_Php_Node
{
public function generate()
{
$this->generator->pushRawHtml($this->node->getValue());
}
}
/**
* Document text data representation.
* @package phptal.php
*/
class PHPTAL_Php_Text extends PHPTAL_Php_Node
{
public function generate()
{
$this->generator->pushString($this->node->getValue());
}
}
/**
* Comment, preprocessor, etc... representation.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Specific extends PHPTAL_Php_Node
{
public function generate()
{
$this->generator->pushHtml($this->node->getValue());
}
}
/**
* Document doctype representation.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Doctype extends PHPTAL_Php_Node
{
public function __construct(PHPTAL_Php_CodeWriter $generator, $node)
{
parent::__construct($generator, $node);
$this->generator->setDocType($this);
}
public function generate()
{;
$this->generator->doDoctype();
}
}
/**
* XML declaration node.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_XmlDeclaration extends PHPTAL_Php_Node
{
public function __construct(PHPTAL_Php_CodeWriter $gen, $node)
{
parent::__construct($gen, $node);
$this->generator->setXmlDeclaration($this);
}
public function generate()
{
$this->generator->doXmlDeclaration();
}
}
?>

152
PHPTAL/Php/State.php Normal file
View file

@ -0,0 +1,152 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Tales.php';
/**
* @package phptal.php
*/
class PHPTAL_Php_State
{
public function __construct()
{
$this->_debug = false;
$this->_talesMode = 'tales';
$this->_encoding = 'UTF-8';
$this->_outputMode = '';
}
public function setDebug($bool)
{
$old = $this->_debug;
$this->_debug = $bool;
return $old;
}
public function isDebugOn()
{
return $this->_debug;
}
public function setTalesMode($mode)
{
$old = $this->_talesMode;
$this->_talesMode = $mode;
return $old;
}
public function getTalesMode()
{
return $this->_talesMode;
}
public function setEncoding($enc)
{
$this->_encoding = $enc;
}
public function getEncoding()
{
return $this->_encoding;
}
public function setOutputMode($mode)
{
$this->_outputMode = $mode;
}
public function getOutputMode()
{
return $this->_outputMode;
}
public function evalTalesExpression($expression)
{
if ($this->_talesMode == 'php')
return PHPTAL_TalesInternal::php($expression);
return phptal_tales($expression);
}
public function interpolateTalesVarsInString($string)
{
if ($this->_talesMode == 'tales'){
return PHPTAL_TalesInternal::string($string);
}
// replace ${var} found in expression
while (preg_match('/(?<!\$)\$\{([^\}]+)\}/ism', $string, $m)){
list($ori, $exp) = $m;
$php = PHPTAL_TalesInternal::php($exp);
$repl = '\'.%s.\'';
$repl = sprintf($repl, $php, $this->_encoding);
$string = str_replace($ori, $repl, $string);
}
$string = str_replace('$${', '${', $string);
return '\''.$string.'\'';
}
private function _interpolateTalesVarsStructure($matches) {
return '<?php echo '.phptal_tale($matches[1]).' ?>';
}
private function _interpolateTalesVarsEscaped($matches) {
return '<?php echo phptal_escape('.phptal_tale($matches[1]).', ENT_QUOTES, \''.$this->_encoding.'\');?>';
}
public function interpolateTalesVarsInHtml($src)
{
if ($this->_talesMode == 'tales'){
$result = preg_replace_callback('/(?<!\$)\$\{structure (.*?)\}/ism', array($this,'_interpolateTalesVarsStructure'), $src);
$result = preg_replace_callback('/(?<!\$)\$\{(.*?)\}/ism', array($this,'_interpolateTalesVarsEscaped'), $result);
$result = str_replace('$${', '${', $result);
return $result;
}
while (preg_match('/(?<!\$)\${(structure )?([^\}]+)\}/ism', $src, $m)){
list($ori, $struct, $exp) = $m;
$php = PHPTAL_TalesInternal::php($exp);
// when structure keyword is specified the output is not html
// escaped
if ($struct){
$repl = '<?php echo '.$php.'; ?>';
}
else {
$repl = '<?php echo '.$this->htmlchars($php).'; ?>';
}
$src = str_replace($ori, $repl, $src);
}
return str_replace('$${','${', $src);
}
public function htmlchars($php)
{
return 'phptal_escape('.$php.', ENT_QUOTES, \''.$this->_encoding.'\')';
}
private $_debug;
private $_talesMode;
private $_encoding;
private $_outputMode;
}
?>

135
PHPTAL/Php/Tales.php Normal file
View file

@ -0,0 +1,135 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
define('PHPTAL_TALES_DEFAULT_KEYWORD', '_DEFAULT_DEFAULT_DEFAULT_DEFAULT_');
define('PHPTAL_TALES_NOTHING_KEYWORD', '_NOTHING_NOTHING_NOTHING_NOTHING_');
// TALES Specification 1.3
//
// Expression ::= [type_prefix ':'] String
// type_prefix ::= Name
//
// Examples:
//
// a/b/c
// path:a/b/c
// nothing
// path:nothing
// python: 1 + 2
// string:Hello, ${username}
//
//
// Builtin Names in Page Templates (for PHPTAL)
//
// * nothing - special singleton value used by TAL to represent a
// non-value (e.g. void, None, Nil, NULL).
//
// * default - special singleton value used by TAL to specify that
// existing text should not be replaced.
//
// * repeat - the repeat variables (see RepeatVariable).
//
function _phptal_tale_wrap($array, $nothrow)
{
if (count($array)==1) return '($ctx->noThrow('.($nothrow?'true':'false').')||1?('.
($array[0]==PHPTAL_TALES_NOTHING_KEYWORD?'NULL':$array[0]).
'):"")';
$expr = array_shift($array);
return "((\$tmp5=$expr) && (\$ctx->noThrow(false)||1)?\$tmp5:"._phptal_tale_wrap($array, $nothrow).')';
}
/** translates array of alternative expressions into single PHP expression. Identical to phptal_tales() for singular expressions. */
function phptal_tale($expression, $nothrow=false)
{
$r = phptal_tales($expression,true);
if (!is_array($r)) return $r;
// this weird ternary operator construct is to execute noThrow inside the expression
return '($ctx->noThrow(true)||1?'._phptal_tale_wrap($r, $nothrow).':"")';
}
function phptal_tales($expression, $nothrow=false)
{
$expression = trim($expression);
// Look for tales modifier (string:, exists:, etc...)
//if (preg_match('/^([-a-z]+):(.*?)$/', $expression, $m)) {
if (preg_match('/^([a-z][-.a-z]*[a-z]):(.*?)$/i', $expression, $m)) {
list(,$typePrefix,$expression) = $m;
}
// may be a 'string'
else if (preg_match('/^\'((?:[^\']|\\\\.)*)\'$/', $expression, $m)) {
$expression = stripslashes($m[1]);
$typePrefix = 'string';
}
// failback to path:
else {
$typePrefix = 'path';
}
// is a registered TALES expression modifier
if(PHPTAL_TalesRegistry::getInstance()->isRegistered($typePrefix)) {
$callback = PHPTAL_TalesRegistry::getInstance()->getCallback($typePrefix);
return call_user_func($callback, $expression, $nothrow);
}
// class method
if (strpos($typePrefix, '.')){
$classCallback = explode('.', $typePrefix, 2);
$callbackName = NULL;
if(!is_callable($classCallback, FALSE, $callbackName)) {
$err = 'Unknown phptal modifier %s function %s does not exists or is not statically callable.';
$err = sprintf($err, $typePrefix, $callbackName);
throw new PHPTAL_Exception($err);
}
$ref = new ReflectionClass($classCallback[0]);
if(!$ref->implementsInterface('PHPTAL_Tales')){
$err = 'Unable to use phptal modifier %s as the class %s does not implement the PHPTAL_Tales interface.';
$err = sprintf($err, $typePrefix, $callbackName);
throw new PHPTAL_Exception($err);
}
return call_user_func($classCallback, $expression, $nothrow);
}
// check if it is implemented via code-generating function
$func = 'phptal_tales_'.str_replace('-','_',$typePrefix);
if (function_exists($func)) {
return $func($expression, $nothrow);
}
// check if it is implemented via runtime function
$runfunc = 'phptal_runtime_tales_'.str_replace('-','_',$typePrefix);
if (function_exists($runfunc)) {
return "$runfunc(".phptal_tale($expression, $nothrow).")";
}
throw new PHPTAL_Exception("Unknown phptal modifier '$typePrefix'. Function '$func' does not exist");
}
// Register internal Tales expression modifiers
require_once PHPTAL_DIR.'PHPTAL/Php/TalesInternal.php';
PHPTAL_TalesInternal::registerInternalTales();
?>

View file

@ -0,0 +1,99 @@
<?php
/**
* @package phptal.php
*/
interface PHPTAL_Php_TalesChainReader
{
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor);
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor);
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $expression, $islast);
}
/**
* @package phptal.php
*/
class PHPTAL_Php_TalesChainExecutor
{
const CHAIN_BREAK = 1;
const CHAIN_CONT = 2;
public function __construct($generator, $chain, $reader)
{
assert(is_array($chain));
$this->_chain = $chain;
$this->_chainStarted = false;
$this->_chainGenerator = $generator;
$this->_reader = $reader;
$this->_executeChain();
}
public function doIf($condition)
{
if ($this->_chainStarted == false){
$this->_chainStarted = true;
$this->_chainGenerator->doIf($condition);
}
else {
$this->_chainGenerator->doElseIf($condition);
}
}
public function doElse()
{
if ($this->_chainStarted){
$this->_chainGenerator->doElse();
}
}
public function breakChain()
{
$this->_state = self::CHAIN_BREAK;
}
public function continueChain()
{
$this->_state = self::CHAIN_CONT;
}
private function _executeChain()
{
$this->_chainGenerator->noThrow(true);
end($this->_chain); $lastkey = key($this->_chain);
foreach ($this->_chain as $key => $exp){
$this->_state = 0;
if ($exp == PHPTAL_TALES_NOTHING_KEYWORD){
$this->_reader->talesChainNothingKeyword($this);
if ($this->_state == self::CHAIN_BREAK)
break;
if ($this->_state == self::CHAIN_CONT)
continue;
}
else if ($exp == PHPTAL_TALES_DEFAULT_KEYWORD){
$this->_reader->talesChainDefaultKeyword($this);
if ($this->_state == self::CHAIN_BREAK)
break;
if ($this->_state == self::CHAIN_CONT)
continue;
}
else {
$this->_reader->talesChainPart($this, $exp, $lastkey === $key);
if ($this->_state == self::CHAIN_BREAK)
break;
if ($this->_state == self::CHAIN_CONT)
continue;
}
}
$this->_chainGenerator->doEnd();
$this->_chainGenerator->noThrow(false);
}
private $_state = 0;
private $_chain;
private $_chainStarted = false;
private $_chainGenerator = null;
}
?>

View file

@ -0,0 +1,331 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
// Moritz Bechler <mbechler@eenterphace.org>
//
require_once PHPTAL_DIR.'PHPTAL/TalesRegistry.php';
class PHPTAL_TalesInternal implements PHPTAL_Tales {
//
// This function registers all internal expression modifiers
//
static public function registerInternalTales() {
static $registered = false;
if($registered) {
return;
}
$registry = PHPTAL_TalesRegistry::getInstance();
$registry->registerPrefix('not', array(__CLASS__, 'not'));
$registry->registerPrefix('path', array(__CLASS__, 'path'));
$registry->registerPrefix('string', array(__CLASS__, 'string'));
$registry->registerPrefix('php', array(__CLASS__, 'php'));
$registry->registerPrefix('exists', array(__CLASS__, 'exists'));
$registry->registerPrefix('number', array(__CLASS__, 'number'));
$registry->registerPrefix('true', array(__CLASS__, 'true'));
$registered = true;
}
static public function true($src, $nothrow)
{
return sprintf('phptal_true($ctx, %s)', self::string(trim($src), $nothrow));
}
//
// not:
//
// not: Expression
//
// evaluate the expression string (recursively) as a full expression,
// and returns the boolean negation of its value
//
// return boolean based on the following rules:
//
// 1. integer 0 is false
// 2. integer > 0 is true
// 3. an empty string or other sequence is false
// 4. a non-empty string or other sequence is true
// 5. a non-value (e.g. void, None, Nil, NULL, etc) is false
// 6. all other values are implementation-dependent.
//
// Examples:
//
// not: exists: foo/bar/baz
// not: php: object.hasChildren()
// not: string:${foo}
// not: foo/bar/booleancomparable
//
static public function not($expression, $nothrow)
{
return '!(' . phptal_tales($expression, $nothrow) . ')';
}
//
// path:
//
// PathExpr ::= Path [ '|' Path ]*
// Path ::= variable [ '/' URL_Segment ]*
// variable ::= Name
//
// Examples:
//
// path: username
// path: user/name
// path: object/method/10/method/member
// path: object/${dynamicmembername}/method
// path: maybethis | path: maybethat | path: default
//
// PHPTAL:
//
// 'default' may lead to some 'difficult' attributes implementation
//
// For example, the tal:content will have to insert php code like:
//
// if (isset($ctx->maybethis)) {
// echo $ctx->maybethis;
// }
// else if (isset($ctx->maybethat) {
// echo $ctx->maybethat;
// }
// else {
// // process default tag content
// }
//
// @returns string or array
//
static public function path($expression, $nothrow=false)
{
$expression = trim($expression);
if ($expression == 'default') return PHPTAL_TALES_DEFAULT_KEYWORD;
if ($expression == 'nothing') return PHPTAL_TALES_NOTHING_KEYWORD;
if ($expression == '') return PHPTAL_TALES_NOTHING_KEYWORD;
// split OR expressions terminated by a string
if (preg_match('/^(.*?)\s*\|\s*?(string:.*)$/sm', $expression, $m)){
list(, $expression, $string) = $m;
}
// split OR expressions terminated by a 'fast' string
else if (preg_match('/^(.*?)\s*\|\s*\'((?:[^\'\\\\]|\\\\.)*)\'\s*$/sm', $expression, $m)){
list(, $expression, $string) = $m;
$string = 'string:'.stripslashes($string);
}
// split OR expressions
$exps = preg_split('/\s*\|\s*/sm', $expression);
// if (many expressions) or (expressions or terminating string) found then
// generate the array of sub expressions and return it.
if (count($exps) > 1 || isset($string)) {
$result = array();
foreach ($exps as $exp) {
$result[] = phptal_tales(trim($exp), true);
}
if (isset($string)){
$result[] = phptal_tales($string, true);
}
return $result;
}
// only one expression to process
// first evaluate ${foo} inside the expression and threat the expression
// as if it was a string to interpolate
$expression = self::string($expression);
$expression = substr($expression, 1, -1);
$pos = strpos($expression, '/');
// if no sub part for this expression, just optimize the generated code
// and access the $ctx->var
if ($pos === false) {
if (!self::checkExpressionPart($expression)) throw new PHPTAL_Exception("Invalid TALES path: '$expression', expected variable name");
return '$ctx->'.$expression;
}
// otherwise we have to call phptal_path() to resolve the path at runtime
// extract the first part of the expression (it will be the phptal_path()
// $base and pass the remaining of the path to phptal_path()
$next = substr($expression, 0, $pos);
$expression = substr($expression, $pos+1);
if (!self::checkExpressionPart($next)) throw new PHPTAL_Exception("Invalid TALES path: '$next/$expression', expected '$next' to be variable name");
// return php code invoking phptal_path($next, $expression, $notrhow)
return 'phptal_path($ctx->'.$next.', \''.$expression.'\''.($nothrow ? ', true' : '').')';
}
private static function checkExpressionPart($expression)
{
return preg_match('/^(\$?[a-z_][a-z0-9_]*|{.*})$/i',$expression);
}
//
// string:
//
// string_expression ::= ( plain_string | [ varsub ] )*
// varsub ::= ( '$' Path ) | ( '${' Path '}' )
// plain_string ::= ( '$$' | non_dollar )*
// non_dollar ::= any character except '$'
//
// Examples:
//
// string:my string
// string:hello, $username how are you
// string:hello, ${user/name}
// string:you have $$130 in your bank account
//
static public function string($expression, $nothrow=false)
{
// This is a simple parser which evaluates ${foo} inside
// 'string:foo ${foo} bar' expressions, it returns the php code which will
// print the string with correct interpollations.
// Nothing special there :)
$inPath = false;
$inAccoladePath = false;
$lastWasDollar = false;
$result = '';
$len = strlen($expression);
for ($i=0; $i<$len; $i++) {
$c = $expression[$i];
switch ($c) {
case '$':
if ($lastWasDollar) {
$lastWasDollar = false;
}
else {
$lastWasDollar = true;
$c = '';
}
break;
case '\\':
$c = '\\\\';
break;
case '\'':
$c = '\\\'';
break;
case '{':
if ($lastWasDollar) {
$lastWasDollar = false;
$inAccoladePath = true;
$subPath = '';
$c = '';
}
break;
case '}':
if ($inAccoladePath) {
$inAccoladePath = false;
$subEval = self::path($subPath);
if (is_array($subEval)) {
$err = 'cannot use | operator in evaluated expressions';
throw new PHPTAL_Exception($err);
}
$result .= "'." . $subEval . ".'";
$subPath = '';
$lastWasDollar = false;
$c = '';
}
break;
default:
if ($lastWasDollar) {
$lastWasDollar = false;
$inPath = true;
$subPath = $c;
$c = '';
}
else if ($inAccoladePath) {
$subPath .= $c;
$c = '';
}
else if ($inPath) {
$t = strtolower($c);
if (($t >= 'a' && $t <= 'z') || ($t >= '0' && $t <= '9') || ($t == '_')){
$subPath .= $c;
$c = '';
}
else {
$inPath = false;
$subEval = self::path($subPath);
if (is_array($subEval)) {
$err = 'cannot use | operator in evaluated expressions';
throw new PHPTAL_Exception($err);
}
$result .= "'." . $subEval . ".'";
}
}
break;
}
$result .= $c;
}
if ($inPath){
$subEval = self::path($subPath);
if (is_array($subEval)){
$err = 'cannot use | operator in evaluated expressions';
throw new PHPTAL_Exception($err);
}
$result .= "'." . $subEval . ".'";
}
return '\''.$result.'\'';
}
/**
* php: modifier.
*
* Transform the expression into a regular PHP expression.
*/
static public function php($src)
{
require_once PHPTAL_DIR.'PHPTAL/Php/Transformer.php';
return PHPTAL_Php_Transformer::transform($src, '$ctx->');
}
/**
* exists: modifier.
*
* Returns the code required to invoke phptal_exists() on specified path.
*/
static public function exists($src, $nothrow)
{
return sprintf('phptal_exists($ctx, %s)', self::string(trim($src), $nothrow));
}
/**
* number: modifier.
*
* Returns the number as is.
*/
static public function number($src, $nothrow)
{
return trim($src);
}
}
?>

384
PHPTAL/Php/Transformer.php Normal file
View file

@ -0,0 +1,384 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* Tranform php: expressions into their php equivalent.
*
* This transformer produce php code for expressions like :
*
* - a.b["key"].c().someVar[10].foo()
* - (a or b) and (c or d)
* - not myBool
* - ...
*
* The $prefix variable may be changed to change the context lookup.
*
* example:
*
* $res = PHPTAL_Php_Transformer::transform('a.b.c[x]', '$ctx->');
* $res == '$ctx->a->b->c[$ctx->x]';
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Transformer
{
const ST_NONE = 0;
const ST_STR = 1; // 'foo'
const ST_ESTR = 2; // "foo ${x} bar"
const ST_VAR = 3; // abcd
const ST_NUM = 4; // 123.02
const ST_EVAL = 5; // ${somevar}
const ST_MEMBER = 6; // abcd.x
const ST_STATIC = 7; // class::[$]static|const
const ST_DEFINE = 8; // @MY_DEFINE
public static function transform( $str, $prefix='$' )
{
//
// Here comes the good old state machine.
// TODO: benchmark this version and then benchmark a refactored version
// with states behaviour separated into methods, keep the fastest.
//
$len = strlen($str);
$state = self::ST_NONE;
$result = '';
$i = 0;
$inString = false;
$backslashed = false;
$instanceOf = false;
$eval = false;
for ($i = 0; $i <= $len; $i++) {
if ($i == $len) $c = "\0";
else $c = $str[$i];
switch ($state) {
// no state defined, just eat char and see what to do with it.
case self::ST_NONE:
// begin of eval without {
if ($c == '$' && $i < $len && self::isAlpha($str[$i+1])){
$state = self::ST_EVAL;
$mark = $i+1;
$result .= $prefix.'{';
}
// that an alphabetic char, then it should be the begining
// of a var
else if (self::isAlpha($c) || $c==='_') {
$state = self::ST_VAR;
$mark = $i;
}
// begining of double quoted string
else if ($c == '"') {
$state = self::ST_ESTR;
$mark = $i;
$inString = true;
}
// begining of single quoted string
else if ($c == '\'') {
$state = self::ST_STR;
$mark = $i;
$inString = true;
}
// closing a method, an array access or an evaluation
else if ($c == ')' || $c == ']' || $c == '}') {
$result .= $c;
// if next char is dot then an object member must
// follow
if ($i < $len-1 && $str[$i+1] == '.') {
$result .= '->';
$state = self::ST_MEMBER;
$mark = $i+2;
$i+=2;
}
}
// @ is an access to some defined variable
else if ($c == '@') {
$state = self::ST_DEFINE;
$mark = $i+1;
}
// character we don't mind about
else {
$result .= $c;
}
break;
// $xxx
case self::ST_EVAL:
if (!self::isVarNameChar($c)){
$result .= $prefix . substr($str, $mark, $i-$mark);
$result .= '}';
$state = self::ST_NONE;
}
break;
// single quoted string
case self::ST_STR:
if ($c == '\\') {
$backslashed = true;
}
else if ($backslashed) {
$backslashed = false;
}
// end of string, back to none state
else if ($c == '\'') {
$result .= substr( $str, $mark, $i-$mark+1 );
$inString = false;
$state = self::ST_NONE;
}
break;
// double quoted string
case self::ST_ESTR:
if ($c == '\\') {
$backslashed = true;
}
else if ($backslashed) {
$backslashed = false;
}
// end of string, back to none state
else if ($c == '"') {
$result .= substr( $str, $mark, $i-$mark+1 );
$inString = false;
$state = self::ST_NONE;
}
// instring interpolation, search } and transform the
// interpollation to insert it into the string
else if ($c == '$' && $i < $len && $str[$i+1] == '{') {
$result .= substr( $str, $mark, $i-$mark ) . '{';
$sub = 0;
for ($j = $i; $j<$len; $j++) {
if ($str[$j] == '{') {
$sub++;
}
elseif ($str[$j] == '}' && (--$sub) == 0) {
$part = substr( $str, $i+2, $j-$i-2 );
$result .= self::transform($part, $prefix);
$i = $j;
$mark = $i;
}
}
}
break;
// var state
case self::ST_VAR:
if (self::isVarNameChar($c)) {
}
// end of var, begin of member (method or var)
else if ($c == '.') {
$result .= $prefix . substr( $str, $mark, $i-$mark );
$result .= '->';
$state = self::ST_MEMBER;
$mark = $i+1;
}
// static call, the var is a class name
else if ($c == ':') {
$result .= substr( $str, $mark, $i-$mark+1 );
$mark = $i+1;
$i++;
$state = self::ST_STATIC;
break;
}
// function invocation, the var is a function name
else if ($c == '(') {
$result .= substr( $str, $mark, $i-$mark+1 );
$state = self::ST_NONE;
}
// array index, the var is done
else if ($c == '[') {
if ($str[$mark]==='_') { // superglobal?
$result .= '$' . substr( $str, $mark, $i-$mark+1 );
}
else {
$result .= $prefix . substr( $str, $mark, $i-$mark+1 );
}
$state = self::ST_NONE;
}
// end of var with non-var-name character, handle keywords
// and populate the var name
else {
$var = substr( $str, $mark, $i-$mark );
$low = strtolower($var);
// boolean and null
if ($low == 'true' || $low == 'false' || $low == 'null') {
$result .= $var;
}
// lt, gt, ge, eq, ...
else if (array_key_exists($low, self::$TranslationTable)){
$result .= self::$TranslationTable[$low];
}
// instanceof keyword
else if ($low == 'instanceof'){
$result .= $var;
$instanceOf = true;
}
// previous was instanceof
else if ($instanceOf){
// last was instanceof, this var is a class name
$result .= $var;
$instanceOf = false;
}
// regular variable
else {
$result .= $prefix . $var;
}
$i--;
$state = self::ST_NONE;
}
break;
// object member
case self::ST_MEMBER:
if (self::isVarNameChar($c)) {
}
// eval mode ${foo}
else if ($c == '$') {
$result .= '{' . $prefix;
$mark++;
$eval = true;
}
// end of var member var, begin of new member
else if ($c == '.') {
$result .= substr( $str, $mark, $i-$mark );
if ($eval) { $result .='}'; $eval = false; }
$result .= '->';
$mark = $i+1;
$state = self::ST_MEMBER;
}
// begin of static access
else if ($c == ':') {
$result .= substr( $str, $mark, $i-$mark+1 );
if ($eval) { $result .='}'; $eval = false; }
$state = self::ST_STATIC;
break;
}
// the member is a method or an array
else if ($c == '(' || $c == '[') {
$result .= substr( $str, $mark, $i-$mark+1 );
if ($eval) { $result .='}'; $eval = false; }
$state = self::ST_NONE;
}
// regular end of member, it is a var
else {
$result .= substr( $str, $mark, $i-$mark );
if ($eval) { $result .='}'; $eval = false; }
$state = self::ST_NONE;
$i--;
}
break;
// wait for separator
case self::ST_DEFINE:
if (self::isVarNameChar($c)) {
}
else {
$state = self::ST_NONE;
$result .= substr( $str, $mark, $i-$mark );
$i--;
}
break;
// static call, can be const, static var, static method
// Klass::$static
// Klass::const
// Kclass::staticMethod()
//
case self::ST_STATIC:
if (self::isVarNameChar($c)) {
}
// static var
else if ($c == '$') {
}
// end of static var which is an object and begin of member
else if ($c == '.') {
$result .= substr( $str, $mark, $i-$mark );
$result .= '->';
$mark = $i+1;
$state = self::ST_MEMBER;
}
// end of static var which is a class name
else if ($c == ':') {
$result .= substr( $str, $mark, $i-$mark+1 );
$state = self::ST_STATIC;
break;
}
// static method or array
else if ($c == '(' || $c == '[') {
$result .= substr( $str, $mark, $i-$mark+1 );
$state = self::ST_NONE;
}
// end of static var or const
else {
$result .= substr( $str, $mark, $i-$mark );
$state = self::ST_NONE;
$i--;
}
break;
// numeric value
case self::ST_NUM:
if (!self::isDigitCompound($c)) {
$result .= substr( $str, $mark, $i-$mark );
$state = self::ST_NONE;
}
break;
}
}
return trim($result);
}
private static function isAlpha($c)
{
$c = strtolower($c);
return $c >= 'a' && $c <= 'z';
}
private static function isDigitCompound($c)
{
return ($c >= '0' && $c <= '9' || $c == '.');
}
private static function isVarNameChar($c)
{
return self::isAlpha($c) || ($c >= '0' && $c <= '9') || $c == '_';
}
private static $TranslationTable = array(
'not' => '!',
'ne' => '!=',
'and' => '&&',
'or' => '||',
'lt' => '<',
'gt' => '>',
'ge' => '>=',
'le' => '<=',
'eq' => '==',
);
}
?>

477
PHPTAL/RepeatController.php Normal file
View file

@ -0,0 +1,477 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* Stores tal:repeat information during template execution.
*
* An instance of this class is created and stored into PHPTAL context on each
* tal:repeat usage.
*
* repeat/item/index
* repeat/item/number
* ...
* are provided by this instance.
*
* 'repeat' is an StdClass instance created to handle RepeatControllers,
* 'item' is an instance of this class.
*
* @package phptal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_RepeatController implements Iterator
{
private $key;
private $current;
private $valid;
private $validOnNext;
protected $iterator;
protected $index;
protected $end;
protected $length;
/**
* Construct a new RepeatController.
*
* @param $source array, string, iterator, iterable.
*/
public function __construct($source)
{
if ( is_string($source) ) {
$this->iterator = new ArrayIterator( str_split($source) ); // FIXME: invalid for UTF-8 encoding, use preg_match_all('/./u') trick
} else if ( is_array($source) ) {
$this->iterator = new ArrayIterator($source);
} else if ( $source instanceof IteratorAggregate ) {
$this->iterator = $source->getIterator();
} else if ( $source instanceof Iterator ) {
$this->iterator = $source;
} else if ( $source instanceof SimpleXMLElement) { // has non-unique keys!
$array = array();
foreach ( $source as $v ) {
$array[] = $v;
}
$this->iterator = new ArrayIterator($array);
} else if ( $source instanceof Traversable || $source instanceof DOMNodeList ) {
// PDO Statements for example implement the engine internal Traversable
// interface. To make it fully iterable we traverse the set to populate
// an array which will be actually used for iteration.
$array = array();
foreach ( $source as $k=>$v ) {
$array[$k] = $v;
}
$this->iterator = new ArrayIterator($array);
} else {
$this->iterator = new ArrayIterator( array() );
}
// Try to find the set length
$this->length = 0;
if ( $this->iterator instanceof Countable ) {
$this->length = count($this->iterator);
} else if ( is_object($this->iterator) ) {
// This should be removed since there is already the Countable interface in PHP5
if ( method_exists( $this->iterator, 'size' ) ) {
$this->length = $this->iterator->size();
} else if ( method_exists( $this->iterator, 'length' ) ) {
$this->length = $this->iterator->length();
}
}
$this->groups = new PHPTAL_RepeatController_Groups();
$this->rewind();
}
/**
* Returns the current element value in the iteration
*
* @return Mixed The current element value
*/
public function current()
{
return $this->current;
}
/**
* Returns the current element key in the iteration
*
* @return String/Int The current element key
*/
public function key()
{
return $this->key;
}
/**
* Tells if the iteration is over
*
* @return bool True if the iteration is not finished yet
*/
public function valid()
{
$valid = $this->valid || $this->validOnNext;
$this->validOnNext = $this->valid;
return $valid;
}
/**
* Restarts the iteration process going back to the first element
*
*/
public function rewind()
{
$this->index = 0;
$this->end = false;
$this->iterator->rewind();
// Prefetch the next element
if ( $this->iterator->valid() ) {
$this->validOnNext = true;
$this->prefetch();
} else {
$this->validOnNext = false;
}
// Notify the grouping helper of the change
$this->groups->reset();
}
/**
* Fetches the next element in the iteration and advances the pointer
*
*/
public function next()
{
$this->index++;
// Prefetch the next element
$this->prefetch();
// Notify the grouping helper of the change
$this->groups->reset();
}
/**
* Gets an object property
*
* @return $var Mixed The variable value
*/
public function __get( $var )
{
switch ( $var ) {
case 'index':
case 'end':
case 'length':
return $this->$var;
case 'number':
return $this->index + 1;
case 'start':
return $this->index === 0;
case 'even':
return ($this->index % 2) === 0;
case 'odd':
return ($this->index % 2) === 1;
case 'key':
return $this->key();
case 'letter':
return strtolower( $this->int2letter($this->index+1) );
case 'Letter':
return strtoupper( $this->int2letter($this->index+1) );
case 'roman':
return strtolower( $this->int2roman($this->index+1) );
case 'Roman':
return strtoupper( $this->int2roman($this->index+1) );
case 'first':
// Compare the current one with the previous in the dictionary
$res = $this->groups->first( $this->current );
return is_bool($res) ? $res : $this->groups;
case 'last':
// Compare the next one with the dictionary
$res = $this->groups->last( $this->iterator->current() );
return is_bool($res) ? $res : $this->groups;
default:
throw new PHPTAL_Exception( "Unable to find part '$var' in repeater controller" );
}
}
/**
* Fetches the next element from the source data store and
* updates the end flag if needed.
*
* @access protected
*/
protected function prefetch()
{
$this->valid = true;
$this->key = $this->iterator->key();
$this->current = $this->iterator->current();
$this->iterator->next();
if ( !$this->iterator->valid() ) {
$this->valid = false;
$this->end = true;
}
}
/**
* Converts an integer number (1 based) to a sequence of letters
*
* @param $int Int The number to convert
* @return String The letters equivalent as a, b, c-z ... aa, ab, ac-zz ...
* @access protected
*/
protected function int2letter( $int )
{
$lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$size = strlen($lookup);
$letters = '';
while ( $int > 0 ) {
$int--;
$letters = $lookup[$int % $size] . $letters;
$int = floor($int / $size);
}
return $letters;
}
/**
* Converts an integer number (1 based) to a roman numeral
*
* @param $int Int The number to convert
* @return String The roman numeral
* @access protected
*/
protected function int2roman( $int )
{
$lookup = array(
'1000' => 'M',
'900' => 'CM',
'500' => 'D',
'400' => 'CD',
'100' => 'C',
'90' => 'XC',
'50' => 'L',
'40' => 'XL',
'10' => 'X',
'9' => 'IX',
'5' => 'V',
'4' => 'IV',
'1' => 'I',
);
$roman = '';
foreach ( $lookup as $max => $letters ) {
while ( $int >= $max ) {
$roman .= $letters;
$int -= $max;
}
}
return $roman;
}
}
/**
* Keeps track of variable contents when using grouping in a path (first/ and last/)
*
* @package phptal
* @author Iván Montes <drslump@pollinimini.net>
*/
class PHPTAL_RepeatController_Groups {
protected $dict = array();
protected $cache = array();
protected $data = null;
protected $vars = array();
protected $branch;
public function __construct()
{
$this->dict = array();
$this->reset();
}
/**
* Resets the result caches. Use it to signal an iteration in the loop
*
*/
public function reset()
{
$this->cache = array();
}
/**
* Checks if the data passed is the first one in a group
*
* @param $data Mixed The data to evaluate
* @return Mixed True if the first item in the group, false if not and
* this same object if the path is not finished
*/
public function first( $data )
{
if ( !is_array($data) && !is_object($data) && !is_null($data) ) {
if ( !isset($this->cache['F']) ) {
$hash = md5($data);
if ( !isset($this->dict['F']) || $this->dict['F'] !== $hash ) {
$this->dict['F'] = $hash;
$res = true;
} else {
$res = false;
}
$this->cache['F'] = $res;
}
return $this->cache['F'];
}
$this->data = $data;
$this->branch = 'F';
$this->vars = array();
return $this;
}
/**
* Checks if the data passed is the last one in a group
*
* @param $data Mixed The data to evaluate
* @return Mixed True if the last item in the group, false if not and
* this same object if the path is not finished
*/
public function last( $data )
{
if ( !is_array($data) && !is_object($data) && !is_null($data) ) {
if ( !isset($this->cache['L']) ) {
$hash = md5($data);
if (empty($this->dict['L'])) {
$this->dict['L'] = $hash;
$res = false;
} else if ( $this->dict['L'] !== $hash ) {
$this->dict['L'] = $hash;
$res = true;
} else {
$res = false;
}
$this->cache['L'] = $res;
}
return $this->cache['L'];
}
$this->data = $data;
$this->branch = 'L';
$this->vars = array();
return $this;
}
/**
* Handles variable accesses for the tal path resolver
*
* @param $var String The variable name to check
* @return Mixed An object/array if the path is not over or a boolean
*
* @todo replace the phptal_path() with custom code
*/
public function __get( $var )
{
// When the iterator item is empty we just let the tal
// expression consume by continuously returning this
// same object which should evaluate to true for 'last'
if ( is_null($this->data) ) {
return $this;
}
// Find the requested variable
$value = @phptal_path( $this->data, $var, true );
// Check if it's an object or an array
if ( is_array($value) || is_object($value) ) {
// Move the context to the requested variable and return
$this->data = $value;
$this->addVarName( $var );
return $this;
}
// get a hash of the variable contents
$hash = md5( $value );
// compute a path for the variable to use as dictionary key
$path = $this->branch . $this->getVarPath() . $var;
// If we don't know about this var store in the dictionary
if ( !isset($this->cache[$path]) ) {
if ( !isset($this->dict[$path]) ) {
$this->dict[$path] = $hash;
$res = $this->branch === 'F';
} else {
// Check if the value has changed
if ( $this->dict[$path] !== $hash ) {
$this->dict[$path] = $hash;
$res = true;
} else {
$res = false;
}
}
$this->cache[$path] = $res;
}
return $this->cache[$path];
}
/**
* Adds a variable name to the current path of variables
*
* @param $varname String The variable name to store as a path part
* @access protected
*/
protected function addVarName( $varname )
{
$this->vars[] = $varname;
}
/**
* Returns the current variable path separated by a slash
*
* @return String The current variable path
* @access protected
*/
protected function getVarPath()
{
return implode('/', $this->vars) . '/';
}
}

16
PHPTAL/Source.php Normal file
View file

@ -0,0 +1,16 @@
<?php
/**
* @package phptal
*/
interface PHPTAL_Source
{
/** Returns string, unique path identifying the template source. */
public function getRealPath();
/** Returns long, the template source last modified time. */
public function getLastModifiedTime();
/** Returns string, the template source. */
public function getData();
}
?>

16
PHPTAL/SourceResolver.php Normal file
View file

@ -0,0 +1,16 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Source.php';
/**
* @package phptal
*/
interface PHPTAL_SourceResolver
{
/**
* Returns PHPTAL_Source or null.
*/
public function resolve($path);
}
?>

34
PHPTAL/StringSource.php Normal file
View file

@ -0,0 +1,34 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Source.php';
/**
* @package phptal
*/
class PHPTAL_StringSource implements PHPTAL_Source
{
public function __construct($data, $realpath)
{
$this->_data = $data;
$this->_realpath = $realpath;
}
public function getLastModifiedTime()
{
if (file_exists($this->_realpath))
return @filemtime($this->_realpath);
return 0;
}
public function getData()
{
return $this->_data;
}
public function getRealPath()
{
return $this->_realpath;
}
}
?>

7
PHPTAL/Tales.php Normal file
View file

@ -0,0 +1,7 @@
<?php
interface PHPTAL_Tales
{
}
?>

109
PHPTAL/TalesRegistry.php Normal file
View file

@ -0,0 +1,109 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Moritz Bechler <mbechler@eenterphace.org>
//
require_once PHPTAL_DIR.'PHPTAL/Tales.php';
/**
* Global registry of TALES expression modifiers
*
*/
class PHPTAL_TalesRegistry {
static $instance;
static public function initialize() {
self::$instance = new PHPTAL_TalesRegistry();
}
/**
* Enter description here...
*
* @return PHPTAL_TalesRegistry
*/
static public function getInstance() {
if(!(self::$instance instanceof PHPTAL_TalesRegistry)) {
self::initialize();
}
return self::$instance;
}
protected function __construct() {
}
/**
*
* Expects an either a function name or an array of class and method as
* callback.
*
* @param unknown_type $prefix
* @param unknown_type $callback
*/
public function registerPrefix($prefix, $callback) {
if($this->isRegistered($prefix)) {
throw new PHPTAL_Exception(sprintf('Expression modifier "%s" is already registered.',$prefix));
}
// Check if valid callback
if(is_array($callback)) {
$class = new ReflectionClass($callback[0]);
if(!$class->isSubclassOf('PHPTAL_Tales')) {
throw new PHPTAL_Exception('The class you want to register does not implement "PHPTAL_Tales".');
}
$method = new ReflectionMethod($callback[0], $callback[1]);
if(!$method->isStatic()) {
throw new PHPTAL_Exception('The method you want to register is not static.');
}
// maybe we want to check the parameters the method takes
} else {
if(!function_exists($callback)) {
throw new PHPTAL_Exception('The function you are trying to register does not exist.');
}
}
$this->_callbacks[$prefix] = $callback;
}
public function isRegistered($prefix) {
return (array_key_exists($prefix, $this->_callbacks));
}
public function getCallback($prefix) {
if(!$this->isRegistered($prefix)) {
throw new PHPTAL_Exception(sprintf('Expression modifier "%s" is not registered.', $prefix));
}
return $this->_callbacks[$prefix];
}
private $_callbacks = array();
}
?>

View file

@ -0,0 +1,41 @@
<?php
/**
* @package phptal
*/
interface PHPTAL_TranslationService
{
/**
* Set the target language for translations.
*
* When set to '' no translation will be done.
*
* You can specify a list of possible language for exemple :
*
* setLanguage('fr_FR', 'fr_FR@euro')
*/
function setLanguage();
/**
* PHPTAL will inform translation service what encoding page uses.
* Output of translate() must be in this encoding.
*/
function setEncoding($encoding);
/**
* Set the domain to use for translations.
*/
function useDomain($domain);
/**
* Set an interpolation var.
*/
function setVar($key, $value);
/**
* Translate a gettext key and interpolate variables.
*/
function translate($key, $htmlescape=true);
}
?>

36
PHPTAL/Trigger.php Normal file
View file

@ -0,0 +1,36 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* @package phptal
*/
interface PHPTAL_Trigger
{
const SKIPTAG = 1;
const PROCEED = 2;
public function start($id, $tpl);
public function end($id, $tpl);
}
?>

24
core/adapter.php Normal file
View file

@ -0,0 +1,24 @@
<?php
/**
* Èíòåðôåéñ ê ìàññèâó è îáüåêòó êàê ê êîëëåêöèè
*/
class Adapter
{
protected $adaptee;
public function __construct ($adaptee)
{
$this->adaptee = $adaptee;
}
public function get ($name)
{
if (is_array ($this->adaptee)) {
return $this->adaptee [$name];
} else {
return $this->adaptee->$name;
}
}
}
?>

7
core/arr.php Normal file
View file

@ -0,0 +1,7 @@
<?php
class Arr {
static function get($data, $key, $default = null) {
return isset($data[$key]) ? $data[$key] : $default;
}
}

100
core/collection.php Normal file
View file

@ -0,0 +1,100 @@
<?php
/**
* Êîëëåêöèÿ
*
* package core
*/
class Collection implements ArrayAccess
{
/**
* Holds collective request data
*
* @var array
*/
protected $data = array();
/**
* Ïðåîáðàçîâàíèå ìàññèâà â êîëëåêöèþ
*
* @param array $data
*/
public function import(array $data)
{
$this->data = array_merge($this->data, $data);
return true;
}
/**
* Ïðåîáðàçîâàíèå êîëëåêöèè â ìàññèâ
*/
public function export()
{
return $this->data;
}
/**
* Store "request data" in GPC order.
*
* @param string $key
* @param mixed $value
*
* @return void
*/
public function set($key, $value)
{
$this->data[$key] = $value;
}
/**
* Read stored "request data" by referencing a key.
*
* @param string $key
*
* @return mixed
*/
public function get($key, $default = null)
{
return isset($this->data[$key]) ? $this->data[$key] : $default;
}
public function getInt($key, $default = 0)
{
return intval($this->get($key, $default));
}
public function getString($key, $default = '')
{
return ((string) $this->get($key, $default));
}
public function getNat($key, $default = 1)
{
$result = intval($this->get($key, $default));
return (($result > 0) ? $result : $default);
}
public function clear()
{
$this->data = array();
}
public function offsetSet($key, $value)
{
$this->data[$key] = $value;
}
public function offsetExists($key)
{
return isset($this->data[$key]);
}
public function offsetUnset($key)
{
unset($this->data[$key]);
}
public function offsetGet($key)
{
return isset($this->data[$key]) ? $this->data[$key] : null;
}
}

4
core/connection/all.php Normal file
View file

@ -0,0 +1,4 @@
<?php
foreach (glob(dirname(__FILE__) . "/*.php") as $file)
require_once $file;
?>

10
core/connection/data.txt Normal file
View file

@ -0,0 +1,10 @@
HTTP/1.1 200 OK
Date: Mon, 31 Mar 2008 12:38:37 GMT
Server: Apache/2.0.61 (Win32) SVN/1.4.3 mod_python/3.3.1 Python/2.4.3 PHP/5.2.5 DAV/2
X-Powered-By: PHP/5.2.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 6232
Connection: close
Content-Type: text/html; charset=windows-1251

View file

@ -0,0 +1,95 @@
<?php
// HttpConncectionRequest
class HttpConnection
{
const POST = "POST";
const GET = "GET";
private $param = array(); // Ïàðàìåòðû çàïðîñà
public $data = null; // Ñîäåðæàíèå
public $url; // Àäðåññ
public $method = self::GET; // Ìåòîä
public $port = 80;
public $host = "";
public $proxy_host = false;
public $proxy_port = false;
/**
* Âîçâðàùàåò çàãîëîâîê ñîåäèíåíèÿ
*/
public function getHeader()
{
$result = $this->method . " " . $this->url . " HTTP/1.1\r\n";
$result .= "Host: ". $this->host ."\r\n";
foreach ($this->param as $key => $value) {
$result .= $key . ": " . $value . "\r\n";
}
$result .= "Connection: Close\r\n\r\n";
$result .= $this->data;
return $result;
}
/**
* Óñòàíîâêà ïàðàìåòðîâ çàïðîñà
* @parma string $name
* @parma string $value
*/
public function setParameter($name, $value)
{
$this->param[$name] = $value;
}
/**
* Ìåòîä çàïðîñà GET èëè POST
*/
public function setMethod($method)
{
$this->method = $method;
}
public function setUrl($url)
{
$this->url = $url;
$this->host = parse_url($this->url, PHP_URL_HOST);
}
public function getUrl()
{
return $this->url;
}
/**
* Ñîäåðæàíèå çàïðîñà
*/
public function setContent($data)
{
$this->setParameter ("Content-length", strlen($data));
$this->data = $data;
}
/**
* Ïîñûëàåò çàïðîñ è âîçâðàùàåò ñòðàíèöó
*/
public function getPage()
{
$host = ($this->proxy_host) ? $this->proxy_host : $this->host;
$port = ($this->proxy_port) ? $this->proxy_port : $this->port;
$socket = fsockopen($host, $port, $errno, $errstr, 30);
if (! $socket) {
return null; // Exception
} else {
$header = $this->getHeader();
fwrite($socket, $header);
$result = null;
while (! feof($socket)) {
$result .= fgets($socket, 128);
}
fclose($socket);
return $result;
}
return null;
}
}

View file

@ -0,0 +1,82 @@
<?php
/**
* Îáðàáàòûâàåò HTTP îòâåò
*/
class HttpConnectionResponse
{
private $offset;
private $param = array ();
private $code;
public function __construct($response)
{
$this->offset = 0;
$this->response = $response;
$this->parseMessage();
}
/**
* Îáðàáîòêà HTTP îòâåòà
*/
private function parseMessage()
{
$http = explode(" ", $this->getLine());
$this->version = $http[0];
$this->code = $http[1];
$line = $this->getLine();
while ($offset = strpos($line, ":")) {
$this->param[substr($line, 0, $offset)] = trim(substr($line, $offset + 1));
$line = $this->getLine();
}
if (isset($this->param['Transfer-Encoding']) && $this->param['Transfer-Encoding'] == 'chunked') {
//$this->data = substr($this->response, $this->offset);
$line = hexdec($this->getLine());
$chunk = array();
while ($line > 0) {
$chunk [] = substr($this->response, $this->offset, $line);
$this->offset += $line;
$line = hexdec($this->getLine());
}
$this->data = implode("", $chunk);
} else {
$this->data = substr($this->response, $this->offset);
}
}
/**
* Îáðàáîòêà ñòðîêè HTTP îòâåòà
*/
private function getLine()
{
$begin = $this->offset;
$offset = strpos($this->response, "\r\n", $this->offset);
$result = substr($this->response, $begin, $offset - $begin);
$this->offset = $offset + 2;
return $result;
}
/**
* Çíà÷åíèå ïàðàìåòðà HTTP îòâåòà
*/
public function getParameter($name)
{
return $this->param[$name];
}
public function getData()
{
return $this->data;
}
/**
* Ñîñòîÿíèå
*/
public function getCode()
{
return $this->code;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,433 @@
<?php
/**
*
* @package Core
*/
require_once 'core/json.php';
require_once 'core/form/form.php';
require_once 'core/controller/controller.php';
require_once 'core/widgets/pagemenu.php';
require_once 'core/widgets/menu.php';
require_once 'core/widgets/search.php';
require_once 'core/widgets/setup.php';
require_once 'core/widgets/listtable.php';
/**
* Ïåðåèìåíîâàòü êîíòðîëëåð !! (StubController, CrudController, PageController, BaseController) ModelController
* Âîçìîæíî íóæåí åùå êëàññ ñ ìåòà äåéñòâèÿìè êàê äëÿ actionIndex <= metaActionIndex ëèáî ñ êëàññàì äëÿ ýòèõ äåéñòâèé
* Åñòü êëàññ äëÿ óïðàâëåíèÿìè äåéñòâèÿìè à åñòü ñàìè äåéñòâèÿ â âèäå êëàññîâ èëè ôóíêöèé !!
*/
class Controller_Model extends Controller_Action
{
public $schema = array();
public $schemaSearch = array();
/**
* FIXME: Ëó÷øå $this->table->setHeader
*/
public $tableSchema = null;
public $formSchema = array();
public $menu;
public $path;
public $table;
public function __construct()
{
$this->path = new PathMenu();
$this->menu = new PageMenu();
$this->table = new ListTable();
}
/**
*/
function setUp()
{
$this->table->addMenuItem($this->aUrl('delete'), 'óäàëèòü', false, 'all', 'warning');
//$this->table->addMenuItem($this->nUrl('form'), 'ðåäàêòèðîâàòü', 'edit-24.png');
}
function saveParameters($args, $list)
{
foreach ($list as $item) {
$args->session()->set(array($this, $item), $args->get($item));
}
}
protected function getJSONList(/*Mapper*/ $model, Collection $request)
{
$result = array();
$this->saveParameters($request, array('size','page','desc', 'key'));
$result['list'] = $model->findAll($request, $request->get('ref'));
$result['size'] = $model->getCount($request, $request->get('ref'));
return json::encode($result);
}
/**
* Óäàëåíèå ñòîðê èç òàáëèöû
*/
public function actionDelete(HttpRequest $request)
{
$model = $this->getModel($this->useModel);
// Ïî÷åìó table_item ???
$list = ($request->get('table_item')) ? $request->get('table_item'): $request->get('id');
$model->deleteList($list);
return $this->getJSONList($model, $request);
}
/**
* Îòâåò íà çàïðîñ ïî ïîèñêó
*/
public function actionSearch(HttpRequest $request)
{
$model = $this->getModel($this->useModel);
$model->addFilter($model->requestToSQL($request, $this->formSchema));
return $this->getJSONList($model, $request);
}
/**
* Ñïèñîê ýëåìåíòîâ
*/
public function actionList(HttpRequest $request)
{
$model = $this->getModel($this->useModel);
return $this->getJSONList($model, $request);
}
private function setFormSchema()
{
require_once 'core/mapper/uimapper.php';
$model = $this->getModel($this->useModel);
$ui = new UIMapper($model);
$this->formSchema = $ui->getFormSchema();
}
/**
* Ñîõðàíåíèå ôîðìû
*/
function beforeSave(/*Model*/ $item, Collection $request)
{
if (empty($this->formSchema)) {
$this->setFormSchema();
}
// Ñäåëàòü îòîáðàæåíèå Ôîðìû â îáüåêò è îáðàòíî <-- Óáðàòü â beforeSave
foreach ($this->formSchema as $key => $conv) {
list($value, $type) = $conv;
$item->$value = call_user_func(array('Cast', 'to_' . $type), $request->get($key)); // Çäåñòü íóæíî ïðåîáðàçîâûâàòü òèï çíà÷åíèÿ
}
}
/**
* Îáíîâëåíèå ôîðìû
*/
function formUpdate(TForm $form, Collection $request)
{
}
/**
* Çàãðóçêà ôîðìû
*/
function beforeLoad(/*Model*/ $item, TForm $form)
{
if (empty($this->formSchema)) {
$this->setFormSchema();
}
// Âñòàâêà çíà÷åíèé èç äàííûõ â ôîðìó
// Îòîáðàæåíèå îáüåêòà â ïîëÿ ôîðìû
$form->fill($item, $this->formSchema);
}
// Ïðîâåðêà ââîäà
protected function validate($validator, $request)
{
}
/**
* Äåéñòâèå äëÿ ïðîâåðêè ôîðìû
*/
public function actionValidate($request)
{
require_once "core/validator/validator.php";
$validator = new Validator();
$validator->addRuleList($this->schema);
// Äåéñòâèÿ äî ïðîâåðêè ôîðìû
$this->validate($validator, $request); // <--|
$validator->validate($request); // --|
// Ïðîâåðêà ôîðìû
if (!$validator->isValid()) {
return json::encode($validator->getErrorMsg());
}
return json::encode(true);
}
/**
* Èíèöèàëèçàöèÿ ôîðìû
*/
protected function formSetup($form, $id = null, $ref = null)
{
if (empty($this->schema)) {
$model = $this->getModel($this->useModel);
$ui = new UIMapper($model);
$schema = $ui->getEditSchema();
$form->addFieldList($schema);
} else {
$form->addFieldList($this->schema);
}
}
/**
* Äîáàâëåíèå ïîëüçîâàòåëÿ
*/
public function actionAdd(HttpRequest $request)
{
require_once "core/validator/validator.php";
// {{{ òîæå ìîæåò áûòü îäèí ref èëè íåñêîëüêî
$ref = $request->get('ref');
$this->addParameter('ref', $ref); // Äîáàâëÿåò ïàðàìåòð â url
/// }}}
if ($this->checkPageId($request, $request->get('page'))) {
// Ïðîâåðêà
$validator = new Validator();
$validator->addRuleList($this->schema);
// Äåéñòâèÿ äî ïðîâåðêè ôîðìû
$this->validate($validator, $request); // <--|
$validator->validate($request); // --|
// Ïðîâåðêà ôîðìû
if (!$validator->isValid()) {
$request->setAction('form');
$this->getActionPath($request);
$form = new TForm();
$this->formSetup($form, $request->get('id'), $request->get('ref')); // Èíèöèàëèçàöèÿ ôîðìû
$form->setValues($request); // <-- Óáðàòü â formUpdate
$this->formUpdate($form, $request);
$form->setError($validator); // Óñòàíîâêà îøèáîê äëÿ ôîðìû
$tpl = $this->formPage($form, $request);
$id = $request->get('id');
if ($id) { // Ðåäàêòèðîâàíèå
$tpl->action = forceUrl($this->nUrl('add', array('id' => $id, 'page' => $this->getPageId($request)))); // action Ñîâéñòâî ôîðìû
}
return $tpl /*->execute()*/;
}
// Íóæåí òåñò äëÿ ôîðìû
$model = $this->getModel($this->useModel);
$className = $model->className;
$item = new $className();
// Ñîõðàíÿåì çíà÷åíèå â áàçå äàííûõ
$item->id = $request->get('id');
// Åñëè òàáëèöà ñâÿçàíà ñ äðóãîé òàáëèöåé
if ($request->get('ref') && $model->reference[1]) {
$ref_id = $model->reference[1];
$item->$ref_id = $request->get('ref');
}
// Ïîäãîòîâêà ê ñîõðàíåíèþ
$this->beforeSave($item, $request); // Ñþäàæå è èñòðèÿ ïåðåõîäîâ
// nextId ??? èëè âûõîä èëè íîâàÿ ôîðìà äëÿ ñîçäàíèÿ íîâîñòè
$model->saveDB($item, $request);
}
// Äëÿ ñòðàíèöû ñî ñïèñêîì id -> èäåíòåôèêàòîð ðîäèòåëüñêîé òàáëèöû !!??
// $request->set('id', $request->get('ref'));
if ($request->get('apply')) {
$request->setAction('form');
return $this->forward('actionForm', $request);
}
return $this->forward('actionIndex', $request);
}
/**
* Çàãîëîâîê
*/
private function setTitlePath($ref)
{
if ($ref) {
$model = $this->getModel($this->useModel);
if (is_array($model->reference) && $model->reference[0]) {
$refmodel = $this->getModel($model->reference[0]);
try {
$parent = $refmodel->findById($ref);
$this->path->addTitle($parent->getTitle()); // Çàãîëîâîê ê ïîäïèñÿì ïóòåé
} catch (Exception $e) {
// Íå íàéäåí çàãîëîâîê ïîòîìó ÷òî íåïðàâèëüíî îïðåäåëåí ðîäèòåëüñêèé ýëåìåíò
}
}
}
}
/**
* Ôîðìà äëÿ ðåäàêòèðîâàíèÿ
*/
public function actionForm(HttpRequest $request)
{
$this->getActionPath($request);
$ref = $request->get('ref');
$this->addParameter('ref', $ref); // Äîáàâëÿåò ïàðàìåòð â url
$this->setTitlePath($ref);
$model = $this->getModel($this->useModel);
$form = new TForm(); // Ïîêàçûâàåì ôîðìó
$form->header = 'Ðåäàêòèðîâàíèå çàïèñè';
$this->formSetup($form, $request->get('id'), $request->get('ref')); // Èíèöèàëèçàöèÿ ôîðìû
$list = $request->get('table_item');
$id = ($list[0]) ? $list[0] : $request->get('id');
$tpl = $this->formPage ($form, $request);
if ($id) { // Ðåäàêòèðîâàíèå
$form->action = forceUrl($this->nUrl('add', array('id' => $id, 'page' => $this->getPageId($request)))); // action Ñâîéñòâî ôîðìû
$item = $model->findById($id);
// Çàãðóçêà ôîðìû
$this->beforeLoad($item, $form);
///
}
return $tpl;
}
/**
*/
function tableSetup($table, $id = null, $ref = null)
{
// FIXME: Ïîñëå çàìåíû âåçäå $tableSchema -> table->setHeader óäàëèòü!
if ($this->tableSchema) {
$table->setHeader($this->tableSchema);
} else {
// Íàñòðîéêà òàáëèöû îòîáðàæåíèÿ ïî ñõåìå äàííûõ
require_once 'core/mapper/uimapper.php';
$model = $this->getModel($this->useModel);
$ui = new UIMapper($model);
$schema = $ui->getTableSchema();
$schema[0]['action'] = $table->getFirstItem();
$table->setHeader($schema);
}
}
/**
*/
public function actionIndex(HttpRequest $request)
{
$this->getActionPath($request, 'index');
// Òàêîå ìåòà äåéñòâèå íàâåðíîå ìîæíî âûíåñòè â îòäåëüíûé êëàññ
return $this->metaActionIndex($request, array($this, 'tableSetup'), $this->aUrl('list'));
}
/**
* Ñòðàíèöà ïî óìîë÷àíèþ
*/
public function metaActionIndex(HttpRequest $request, $setup, $list)
{
// ìîæåò áûòü îäíî ref èëè íåñêîëüêî
// {{{ èñòîðèÿ ïåðåõîäîâ
$ref = null;
if ($request->get('ref')) {
$ref = $request->get('ref');
} else if ($request->session()->get('ref')) {
$ref = $request->session()->get('ref');
}
$request->session->set('ref', $ref);
$this->addParameter('ref', $ref);
// }}}
$this->setTitlePath($ref);
$tpl = $this->getView('list');
// Ïîìîøíèêè äåéñòâèé
$this->callHelpers($request);
// Òàáëèöà
if ($request->session()->get(strtolower(get_class($this)))) {
$session = $request->session()->get(strtolower(get_class($this)));
if (isset($session['view'])) {
$this->table->setView($session['view']);
}
$this->table->setData('state', array(
'page' => $session['page'],
'size' => $session['size'],
'desc' => $session['desc']));
$this->table->setData('sorter', $session['key']);
if (isset($session['desc'])) {
$this->table->setData('desc', $session['desc']);
}
}
call_user_func($setup, $this->table, $request->get('id'), $ref);// --> Ýêâèâàëåíò formSetup
$this->table->setAction($list);
//
$tpl->menu_path = $this->path->getItems();
// Ïîèñê
$search = new SearchDialog();
$search->setTitle('Ïîèñê');
$search->setAction($this->aUrl('search'));
$search->setFriend($this->table);
$search->addFields($this->schemaSearch);
// Íàñòðîéêè
$setup = new SetupDialog();
$setup->setTitle('Íàñòðîéêè');
$setup->setAction($this->nUrl('setup'));
$setup->setFriend($this->table);
// Ìåíþ
$this->menu->addMenuItem('?menu=toggle&id=' . $search->getName(), 'ïîèñê', 'actions/system-search'); // Ñòàíäàðòíûé ðàçìåð äëÿ èêîíîê 22-24px
$this->menu->addMenuItem('?menu=toggle&id=' . $setup->getName(), 'íàñòðîéêè', 'categories/applications-system');
// Äîáàâëåíèå êîìïîíåíòîâ
$this->addChild('menu', $this->menu);
$this->addChild('search', $search);
$this->addChild('setup', $setup);
$this->addChild('table', $this->table);
//
return $tpl;
}
/**
*/
public function actionSetup($request)
{
$left = explode(",", $request->get('left'));
$right = explode(",", $request->get('right'));
$$request->session()->set(strtolower(get_class($this)),
array('view' => array('left' => $left, 'right' => $right)));
return $this->forward('actionIndex', $request);
}
/**
*/
private function formPage($form, $request)
{
$view = $this->getView('form');
$view->setView('form', $form);
$view->action = forceUrl($this->nUrl('add', array('page' => $this->getPageId($request)))); // Äåéñòâèå äëÿ ôîðìû
$view->menu_path = $this->path->getItems();
$view->back = $this->path->getPrev();
return $view;
}
// Òîæå óáðàòü â ìåòîä Controller_Model
function getActionPath(HttpRequest $request/*, $action = false*/)
{
require_once 'state.php';
$this->_getActionPath()->getPath($this, ($action) ? $action : $request->getAction());
}
}

View file

@ -0,0 +1,185 @@
<?php
require_once 'core/path.php';
class FileNotFountException extends Exception
{
}
/**
* Êëàññ êîìïîíåíòà
*/
class Component
{
static $_uid = 1;
public $uid; // UID êîìïîíåíòà ñîçäàåòñÿ ïðè ñîçäàíèè ñòðàíèöû, âñòàâêè êîìïîíåíòà, èëè ýòî ñòàòè÷åñêîå ñâîéñòâî
public $viewPath;
public $registry; // Registry->getInstance
public $template;
function __construct()
{
self::$_uid ++;
$this->uid = self::$_uid;
}
function getUID()
{
return 'component:'. $this->uid;
}
public function getView($name)
{
require_once "core/view/compositeview.php";
//
$template = ($this->template) ? $this->template : $this->_registry->readKey(array('system', 'template'));
// Çàãðóæàòü øàáëîí ïî óìîë÷àíèþ åñëè íå íàéäåí òåêóùèé
if (is_dir(Path::join($this->viewPath, 'templates', $template))) {
$template_file = Path::join($this->viewPath, 'templates', $template, $name);
} else {
$template_file = Path::join($this->viewPath, 'templates', 'modern', $name);
}
$tpl = new View_Composite($template_file);
$tpl->script = $_script = Path::join(WWW_PATH, 'js');
$tpl->media = $_media = Path::join(TEMPLATE_WEB, $template);
$tpl->component = $_template = Path::join(COMPONENTS_WEB, strtolower(get_class($this)), 'templates', 'modern');
$tpl->setAlias(array(
'${media}' => $_media,
'${script}' => $_script,
'${template}' => $_template));
$tpl->loadImports(Path::skipExtension($template_file) . ".import");
return $tpl;
}
public function setParameters($view)
{
}
/**
* @param $name Èìÿ ìîäåëè
*/
private function getModelPath($name)
{
return Path::join (CMS_PATH, "model", $name . ".php");
}
/**
* Ñîçäàåò ìîäåëü
* @param string $name
* @return model
*/
public function getModel($name)
{
require_once 'core/mapper/mapper.php';
require_once ($this->getModelPath ($name));
$modelName = $name . "Mapper";
$model = new $modelName ();
$model->db = $this->db;
return $model;
}
public function options($key, $val, $res) {
$result = array();
while($res->next()) {
$result[] = array('value' => $res->getInt($key), 'name' => $res->getString($val));
}
return $result;
}
public function optionsPair($list) {
$result = array();
foreach ($list as $key => $value) {
$result [] = array('value' => $key, 'name' => $value);
}
return $result;
}
/* Â äàëüíåéøåì íóæíî çìåíèòü íà ìåòîäû
+ Ìåòîäû ìîãóò áûòü è javascript
*/
protected $editUrl;
function setEditUrl($url)
{
$this->editUrl = $url;
}
function getEditUrl()
{
return $this->editUrl;
}
}
/**
* TALES äëÿ ïîäêëþ÷åíèÿ êîìïîíåíòîâ
* component:name?param1=value1&param2=value2
*/
class Component_Tales implements PHPTAL_Tales
{
static public function component($expression, $nothrow = false)
{
return "phptal_component('" . $expression . "')";
}
}
function loadComponent($name, $db, $registry)
{
$path = Path::join(COMPONENTS, $name, $name . ".php");
// echo COMPONENTS, '<br />';
// echo $path;
if (file_exists($path)) {
require_once ($path);
$component = new $name();
$component->db = $db;
$component->_registry = $registry;
$component->viewPath = COMPONENTS."/".$name."/";
return $component;
}
throw new FileNotFountException();
}
/**
* Ôóíêöèÿ ïîäêëþ÷åíèÿ êîìïîíåíòà
*/
global $componentList;
$componentList = array();
function phptal_component ($real_expression, $offset = 0) {
global $db, $registry, $componentList; // Íóæíî êàêòî ïåðåäàâàòü ïàðàìåòðû
$expression = htmlspecialchars_decode($real_expression);
$url = parse_url($expression);
parse_str($url['query'], $arguments);
$name = $url['path'];
$component = loadComponent($name, $db, $registry);
$req = new HttpRequest();
$params = new Collection();
$params->import(array_merge($_GET, $arguments));
$component->params = $params;
$componentList [] = array(
'uid' => $component->getUID(), 'params' => $expression, 'name' => $name, 'offset' => $offset,
'size' => strlen($real_expression),
/* Âìåñòî ññûëêè íà ðåäàêòèðîâàíèå íóæíî ïåðåäàâàòü ñïèñîê ìåòîäîâ äëÿ ðàáîòû ñ êîìïîíåíòîì
edit (ðåäàêòèðîâàíèå ñîäåðæàíèå), new (íîâîå ñîäåðæàíèå), øàáëîí êîìåííåíòà ... âìåñòå ñ èêîíêàìè ýòèõ ìåòîäîâ
! Êîìïîíåíòû ìîãóò ñîäåðæàòü äðóãèå êîìïîíåíòû
*/
'editurl' => $component->getEditUrl(),
'newurl' => ''
);
unset($req['active_page']);
$component->template = $params->get('template', false);
return $component->execute($params, $req);
}
/* Ðåãèñòðàöèÿ íîâîãî ïðåôèêñà äëÿ ïîäêëþ÷åíèÿ êîìïîíåíòà */
$registry = PHPTAL_TalesRegistry::getInstance();
$registry->registerPrefix('component', array('Component_Tales', 'component'));

View file

@ -0,0 +1,334 @@
<?php
require_once 'core/path.php';
require_once 'core/mapper/factory.php';
require_once 'core/functions.php';
function forceUrl($name)
{
if (is_callable($name)) {
return call_user_func($name);
}
return $name;
}
/**
* Êîíòðîëëåð ñòðàíèö
* @package core
*/
class Controller
{
const TEMPLATE_EXTENSION = ".html"; // Ðàñøèðåíèå äëÿ øàáëîíîâ
const ACTION_PREFIX = "action"; // Ïðåôèêñ äëÿ ôóíêöèé äåéñòâèé
public $jsPath; // Ãëîáàëüíûé ïóòü ê ñêðèïòàì
public $themePath; // Ãëîáàëüíûé ïóòü ê òåêóùåé òåìå
// Ïàðàìåòðû óñòàíàâëèâàþòñÿ ïðè ñîçäàíèè êîíòðîëëåðà
public $name; // Èìÿ ìîäóëÿ
public $viewPath = null; // Ïóòü ê øàáëîíàì êîíòðîëëåðà
public $db; // Ñîåäèíåíèå ñ áàçîé äàííûõ
// Ôèëüòðû
public $access; // Îáüåêò õðàíèò ïàðàìåòðû äîñòóïà
public $logger; // Îáüåêò äëÿ âåäåíèÿ ëîãà
private $factory; // Ññûëêà íà îáüåêò ñîçäàíèÿ ìîäåëè
private $helpers = array(); // Ïîìîøíèêè äëÿ äåéñòâèé
public $param = array(); // Ïàðàìåòðû äëÿ ññûëêè
public $_registry; // Ññûëêà íà ðååñòð
public $_shortcut;
public function __construct ()
{
//
}
public function setUp ()
{
// override this
}
public function loadConfig($name) {
$filename = Shortcut::getUrl('config', $this->name, $name);
include($filename);
return $settings;
}
public function getConnection()
{
return $this->db;
}
public function installPath($name)
{
return Path::join(CMS_PATH, "modules", $name, "install");
}
public function addSuggest($view, $name)
{
$suggest = array();
$file = Path::join($this->viewPath, 'help', $name . '.suggest');
if (file_exists($file) && include($file)) {
$view->addScriptRaw("add_suggest(".json::encode($suggest).");\n");
}
}
function findIcon($icon, $size)
{
return Path::join($this->iconPath, $size . 'x' . $size, $icon . '.png');
}
/**
* Ñîçäàåò ïðåäñòàâëåíèå
* @param string $file
* @return template
*/
public function getView($name)
{
require_once "core/view/compositeview.php";
$file = $name . self::TEMPLATE_EXTENSION;
// Ñïèñîê âîçìîæíûõ äèðåêòîðèé äëÿ ïîèñêà ôàéëà øàáëîíà
$theme = $this->_registry->readKey(array('system', 'theme'));
$icon_theme = $this->_registry->readKey(array('system', 'icon_theme'));
$list = array(
Path::join($this->viewPath, TEMPLATES) => Path::join(WWW_PATH, "modules", $this->name, TEMPLATES),
PHPTAL_TEMPLATE_REPOSITORY => "");
// Ïîèñê ôàéëà äëÿ øàáëîíà
foreach($list as $ospath => $path) {
$template = Path::join($ospath, $file);
if(file_exists($template)) { break; }
}
$tpl = new View_Composite($template);
$tpl->icons = $this->iconPath; // Ïóòü ê ôàéëàì òåêóùåé òåìû
$tpl->media = $this->themePath; // Ïóòü ê ôàéëàì òåêóùåé òåìû
$tpl->script = $this->jsPath; // Ïóòü ê ôàéëàì ñêðèïòîâ
$tpl->template = $path; // Ïóòü ê ôàéëàì òåêóùåãî øàáëîíà
$tpl->setAlias(array(
'${icons}' => $this->iconPath,
'${media}' => $this->themePath,
'${script}' => $this->jsPath,
'${template}' => $path));
$tpl->loadImports(Path::skipExtension($template) . ".import");
$this->addSuggest($tpl, $name);
return $tpl;
}
public function getModel($name)
{
if (!$this->factory) {
$this->factory = new ModelFactory($this->db, $this->_registry, $this->_shortcut);
}
return $this->factory->getModel($name);
}
/**
* Âûáîð äåéñòâèÿ
* Ò.ê äåéñòâèÿ ÿâëÿþòñÿ ìåòîäàìè êëàññà òî
* 1. Ìîæíî ïåðåîïðåäåëèòü äåéñòâèÿ
* 2. Èñïîëüçîâàòü íàñëåäîâàíèå ÷òîáû äîáàâèòü ê ñòàðîìó îáðàáîò÷èêó íîâîå ïîâåäåíèå
* @param $request Îáüåêò çàïðîñà
*/
public function execute1(HTTPRequest $request)
{
$action = self::ACTION_PREFIX . ucfirst($request->getAction());
if (method_exists($this, $action)) {
return $this->forward($action, $request);
} else {
return $this->forward("actionIndex", $request);
}
}
public function execute(HTTPRequest $request)
{
$result = $this->execute1($request);
if ($result) {
$this->view = $result;
}
return $this->render();
}
public function forward($action, HTTPRequest $args)
{
// Äåéñòâèÿ äî âûçîâà îñíîâíîãî îáðàáîò÷èêà
/*foreach($this->_aspect as $aspect) {
if (isset($aspect->before[$action])) {
call_user_func ($aspect->before[$action], $action, $args);
}
}*/
return call_user_func(array($this, $action), $args);
}
/**
* Ñòðàíèöà ïî óìîë÷àíèþ
*/
public function actionIndex(HttpRequest $request)
{
return "";
}
public function postUrl($name, $param)
{
return "?" . http_build_query(
array_merge(array('module' => strtolower(get_class($this)), "action" => $name),
$this->param, $param));
}
/**
* Ãåíåðàöèÿ ññûëêè c ó÷åòîì ïðàâ ïîëüçîâàòåëÿ íà ññûëêè
*
* @parma string $name Äåéñòâèå
* @parma string $param Äîïîëíèòåëüíûå ïàðàìåòðû
*/
public function nUrl($name, array $param = array())
{
if (!$this->access || $this->access->checkAction($name)) {
return lcurry(array($this, 'postUrl'), $name, $param);
}
return null;
}
public function fUrl($name, array $param = array())
{
return forceUrl($this->nUrl($name, $param));
}
/**
* Äîáàâëÿåò ïàðàìåòð äëÿ âñåõ ññûëîê ñîçäàâàåìûõ ôóíêöèåé nUrl, aUrl
*/
public function addParameter($name, $value)
{
if ($value) {
$this->param [$name] = $value;
}
}
/**
* Ãåíåðàöèÿ ññûëêè íà äåéñòâèå êîíòðîëëåðà
* Ajax îïðåäåëÿåòñÿ àâòîìàòè÷åñêè mode = ajax èñïîëüçóåòñÿ äëÿ ñìåíû layout
*/
public function aUrl($name, array $param = array())
{
return $this->nUrl($name, array_merge(array('mode' => 'ajax'), $param)); // FIXME
}
/**
* Äîáàâëåíèå ïîìîøíèêà êîíòðîëëåðà
*/
public function addHelper($class)
{
$this->helpers [] = $class;
}
/**
* Âûçîâ ïîìîøíèêîâ êîíòðîëëåðà
*/
public function callHelpers(HttpRequest $request)
{
$action = self::ACTION_PREFIX . $request->getAction();
foreach ($this->helpers as $helper) {
if (method_exists($helper, $action)) {
return call_user_func(array($helper, $action), $request, $this);
} else {
return $helper->actionIndex($request, $this); // Âìåñòî return response ???
}
}
}
/**
* Çàãðóçêà ôàéëà êëàññà
*/
public function loadClass($path, $setup = null)
{
if (file_exists($path)) {
require_once ($path);
$class = pathinfo($path, PATHINFO_FILENAME);
return new $class($setup);
}
return null;
}
public function loadSettings($path)
{
$result = new Settings($path);
$result->read();
return $result->export();
}
// Äëÿ Widgets
public $view = null;
public $childNodes = array();
public $childViews = array();
public function setView($name)
{
$this->view = $this->getView($name);
}
/**
* Óñòàíîâêà çàãîëîâêà äëÿ îòîáðàæåíèÿ
*/
public function setTitle($title)
{
$this->view->setTitle($title);
}
/**
* Äîáàâëåíèå widget ê îòîáðàæåíèþ
*/
public function addChild(/*Widget*/ $section, $node)
{
$this->childNodes[$section] = $node;
}
/**
* Äîáàâëåíèå äî÷åðíåãî îòîáðàæåíèÿ ê òåêóùåìó îòîáðàæåíèþ
*/
public function addView(/*CompositeView*/ $section, $node)
{
$this->childViews[$section] = $node;
}
/**
* Ãåíåðàöèÿ ñîäåðæàíèÿ
* Ïóòàíèöà c execute è render
*/
public function render()
{
foreach ($this->childNodes as $name => $node) {
$node->make($this);
$this->view->setView($name, $node->view);
}
foreach ($this->childViews as $name => $node) {
$this->view->setView($name, $node);
}
return $this->view;
}
function getPageId($request)
{
$pageId = time();
$request->session()->set('page', $pageId);
return $pageId;
}
function checkPageId($request, $page)
{
$_page = $request->session()->get('page');
$result = ($_page && $_page == $page);
$request->session()->clean('page');
return $result;
}
}
class Controller_Action extends Controller {}

View file

@ -0,0 +1,92 @@
<?php
require_once 'core/controller/controller.php';
require_once 'core/controller/installer.php';
/**
* Ïåðâè÷íûé êîíòðîëëåð êîíòðîëëåð ñòðàíèö
* @package core
*/
class Controller_Front extends Controller
{
protected $shortcut; // ßðëûê ê ìîäóëþ
protected $_param; // Ïàðàìåòð ïî êîòîðîìó âûáèðàåòñÿ ìîäóëü
protected $default; // Çíà÷åíèå ïàðàìåòðà ïî óìîë÷àíèþ
protected $installer;
public function __construct(Settings $_registry, $_shortcut)
{
require_once 'creole/Creole.php';
parent::__construct();
$registry = $_registry;
$this->_registry = $_registry;
$this->_shortcut = $_shortcut;
$this->db = Creole::getConnection($registry->readKey(array('system', 'dsn')));
$this->installer = new Installer($_registry);
}
/**
* Ñîçäàåò ýêçåìïëÿð ìîäóëÿ è âûïîëíÿåò äåéñòâèÿ äëÿ íåãî
* @param string $name Èìÿ ìîäóëÿ
* @param request $request Èìÿ ìîäóëÿ
* @return string
*/
public function loadModule($name, Collection $request)
{
$this->installer->setUp($this->db, array($this, 'installPath'));
$this->installer->doUpdates($name); // ModuleLoader (1)
$moduleFile = Shortcut::getUrl($this->shortcut, $name); // ModuleLoader (2)
$module = $this->loadClass($moduleFile);
if ($module) {
// Èíèöèàëèçàöèÿ ìîäóëÿ
// $module->viewPath = dirname($moduleFile);
$module->viewPath = Shortcut::getUrl('modulepath', $name);
$module->name = $name;
$module->param = $this->param;
//
$module->_registry = $this->_registry;
$module->_shortcut = $this->_shortcut;
$module->iconPath = $this->iconPath; // -> Registry
$module->themePath = $this->themePath; // -> Registry
$module->jsPath = $this->jsPath; // -> Registry
$module->db = $this->db;
// Íå äëÿ âñåõ ïðèëîæåíèé íóæíî âåñòè ëîã äåéñòâèé
// Âåäåíèå ëîãà
$logger = $this->loadClass(FRAMEWORK_PATH . '/core/filter/actionlogger.php', $module);
$logger->before = $this->loadSettings(Shortcut::getUrl('logger', $name));
// Óïðàâëåíèå äîñòóïîì
$module->access = $this->loadClass(FRAMEWORK_PATH . '/core/filter/actionaccess.php', $logger);
$module->access->access = $this->loadSettings(Shortcut::getUrl('access', $name));
$module->setUp();
return $module->access->execute($request);
}
return null; // throw new FileNotFoundException();
}
public function setParameter($shortcut, $param, $name)
{
$this->shortcut = $shortcut;
// Ïàðàìåòð
$this->_param = $param;
$this->default = $name;
}
private function getParameter(Collection $list)
{
return ($list->get($this->_param)) ? $list->get($this->_param): $this->default;
}
public function execute(HTTPRequest $request)
{
return $this->loadModule($this->getParameter($request), $request);
}
}

View file

@ -0,0 +1,89 @@
<?php
require_once 'core/settings.php';
class Installer
{
protected $db;
protected $installPath;
public $_registry;
public function __construct(Settings $_registry)
{
$this->_registry = $_registry;
}
public function setUp($db, $installPath)
{
$this->db = $db;
$this->installPath = $installPath;
}
function getSetupFile($name)
{
return Path::join(call_user_func($this->installPath, $name), "setup.php");
}
// Ïðîâåðêà âåðñèè îáíîâëåíèÿ
function isChanged($name) // Èíôîðìàöèÿ î ìîäóëÿõ
{
$item = $this->_registry->readKey(array($name));
if ($item) {
$setup = $this->getSetupFile($name);
if (file_exists($setup) && (filemtime($setup) > $item['time'])) {
return true;
}
return false;
}
return true;
}
function installSQL(array $sql, $version_new, $version_old, $name)
{
require_once "core/setup.php";
foreach ($sql as $version => $install) {
if (version_compare($version, $version_new, "<=") && version_compare($version, $version_old, ">")) {
// this->installPath this->db
$file = Path::join(call_user_func($this->installPath, $name), "sql", $install);
Setup::batchSQL($this->db, $file);
}
}
}
// Óñòàíàâëèâàåò îáíîâëåíèÿ åñëè åñòü
function doUpdates($name, $force = false) // Óñòàíîâêà ìîäóëÿ
{
$setup = $this->getSetupFile($name);
if (file_exists($setup) && ($this->isChanged($name) || $force)) {
$registry = $this->_registry;
$settings = new Settings($setup);
$settings->read();
$item = $registry->readKey(array($name));
$version_new = $settings->get('version');
if ($item) {
$version_old = $item['version'];
} else {
$version_old = "0.0";
$registry->writeKey(array($name), array());
}
if (version_compare($version_old, $settings->get('version'), "!=")) {
$sql = $settings->get('sql');
if (is_array($sql)) {
$this->installSQL($sql, $version_new, $version_old, $name);
}
}
// Îáíîâëåíèå âåðñèè ìåíþ
$registry->writeKey(array($name), $settings->get('settings'));
$registry->writeKey(array($name),
array('version' => $version_new,
'time' => filemtime($setup)));
$registry->write();
}
}
}

79
core/controller/state.php Normal file
View file

@ -0,0 +1,79 @@
<?php
class State
{
public $action = '';
public $states = array();
public $titles = array();
public function __construct($action)
{
$this->action = $action;
}
static function make($action)
{
return new State($action);
}
public function addTitle($name, $url = array())
{
$this->titles [] = array($name, $url);
return $this;
}
public function addState(State $state)
{
$this->states [$state->getAction()] = $state;
return $this;
}
public function getAction()
{
return $this->action;
}
function checkAction($action, &$list)
{
if ($this->action == $action) {
array_push($list, $this);
return true;
} else {
foreach ($this->states as $state) {
if ($state->checkAction($action, $list)) {
array_push($list, $this);
return true;
}
}
}
return false;
}
function makeTitle($module)
{
foreach ($this->titles as $item) {
$module->path->addMenuItem($module->nUrl($this->action, $item[1]), $item[0]);
}
}
function getPath($module, $action)
{
$list = array();
if ($this->checkAction($action, $list)) {
foreach (array_reverse($list) as $item) {
$item->makeTitle($module);
}
} else {
$this->makeTitle($module);
}
}
}
/*
$path = State::make('index')
->addState(State::make('form'))
->addState(State::make('view'));
$path->getPath(0, 'form');
*/

26
core/data/areas.php Normal file
View file

@ -0,0 +1,26 @@
<?php
$Areas = array
(
0 => '',
1 => 'Город Ярославль',
2 => 'Большесельский район',
3 => 'Борисоглебский район',
4 => 'Брейтовский район',
5 => 'Гаврилов-Ямский район',
6 => 'Даниловский район',
7 => 'Любимский район',
8 => 'Мышкинский район',
9 => 'Некоузский район',
10 => 'Некрасовский район',
11 => 'Переславский район',
12 => 'Первомайский район',
13 => 'Пошехонский район',
14 => 'Ростовский район',
15 => 'Рыбинский район',
16 => 'Тутаевский район',
17 => 'Угличский район',
18 => 'Ярославский район',
19 => 'Город Переславль',
20 => 'Город Рыбинск'
);

28
core/data/city.php Normal file
View file

@ -0,0 +1,28 @@
<?php
global $TypeCity, $TypeCityShort;
// Массив типов поселений
$TypeCity = array(
// 0 => '',
1 => 'город',
2 => 'село',
3 => 'поселок',
4 => 'деревня',
5 => 'поселок городского типа',
6 => 'рабочий поселок',
7 => 'станица',
8 => 'аул'
);
$TypeCityShort = array(
0 => '',
1 => 'г.',
2 => 'с.',
3 => 'п.',
4 => 'д.',
5 => 'пгт.',
6 => 'р.п.',
7 => 'ст.',
8 => 'а.'
);

196
core/data/mime.php Normal file
View file

@ -0,0 +1,196 @@
<?php
/**
* http://www.w3schools.com/media/media_mimeref.asp
*/
$_mime_type = array (
"" => "application/octet-stream",
"323" => "text/h323",
"acx" => "application/internet-property-stream",
"ai" => "application/postscript",
"aif" => "audio/x-aiff",
"aifc" => "audio/x-aiff",
"aiff" => "audio/x-aiff",
"asf" => "video/x-ms-asf",
"asr" => "video/x-ms-asf",
"asx" => "video/x-ms-asf",
"au" => "audio/basic",
"avi" => "video/x-msvideo",
"axs" => "application/olescript",
"bas" => "text/plain",
"bcpio" => "application/x-bcpio",
"bin" => "application/octet-stream",
"bmp" => "image/bmp",
"c" => "text/plain",
"cat" => "application/vnd.ms-pkiseccat",
"cdf" => "application/x-cdf",
"cer" => "application/x-x509-ca-cert",
"class" => "application/octet-stream",
"clp" => "application/x-msclip",
"cmx" => "image/x-cmx",
"cod" => "image/cis-cod",
"cpio" => "application/x-cpio",
"crd" => "application/x-mscardfile",
"crl" => "application/pkix-crl",
"crt" => "application/x-x509-ca-cert",
"csh" => "application/x-csh",
"css" => "text/css",
"dcr" => "application/x-director",
"der" => "application/x-x509-ca-cert",
"dir" => "application/x-director",
"dll" => "application/x-msdownload",
"dms" => "application/octet-stream",
"doc" => "application/msword",
"dot" => "application/msword",
"dvi" => "application/x-dvi",
"dxr" => "application/x-director",
"eps" => "application/postscript",
"etx" => "text/x-setext",
"evy" => "application/envoy",
"exe" => "application/octet-stream",
"fif" => "application/fractals",
"flr" => "x-world/x-vrml",
"gif" => "image/gif",
"gtar" => "application/x-gtar",
"gz" => "application/x-gzip",
"h" => "text/plain",
"hdf" => "application/x-hdf",
"hlp" => "application/winhlp",
"hqx" => "application/mac-binhex40",
"hta" => "application/hta",
"htc" => "text/x-component",
"htm" => "text/html",
"html" => "text/html",
"htt" => "text/webviewhtml",
"ico" => "image/x-icon",
"ief" => "image/ief",
"iii" => "application/x-iphone",
"ins" => "application/x-internet-signup",
"isp" => "application/x-internet-signup",
"jfif" => "image/pipeg",
"jpe" => "image/jpeg",
"jpeg" => "image/jpeg",
"jpg" => "image/jpeg",
"js" => "application/x-javascript",
"latex" => "application/x-latex",
"lha" => "application/octet-stream",
"lsf" => "video/x-la-asf",
"lsx" => "video/x-la-asf",
"lzh" => "application/octet-stream",
"m13" => "application/x-msmediaview",
"m14" => "application/x-msmediaview",
"m3u" => "audio/x-mpegurl",
"man" => "application/x-troff-man",
"mdb" => "application/x-msaccess",
"me" => "application/x-troff-me",
"mht" => "message/rfc822",
"mhtml" => "message/rfc822",
"mid" => "audio/mid",
"mny" => "application/x-msmoney",
"mov" => "video/quicktime",
"movie" => "video/x-sgi-movie",
"mp2" => "video/mpeg",
"mp3" => "audio/mpeg",
"mpa" => "video/mpeg",
"mpe" => "video/mpeg",
"mpeg" => "video/mpeg",
"mpg" => "video/mpeg",
"mpp" => "application/vnd.ms-project",
"mpv2" => "video/mpeg",
"ms" => "application/x-troff-ms",
"mvb" => "application/x-msmediaview",
"nws" => "message/rfc822",
"oda" => "application/oda",
"p10" => "application/pkcs10",
"p12" => "application/x-pkcs12",
"p7b" => "application/x-pkcs7-certificates",
"p7c" => "application/x-pkcs7-mime",
"p7m" => "application/x-pkcs7-mime",
"p7r" => "application/x-pkcs7-certreqresp",
"p7s" => "application/x-pkcs7-signature",
"pbm" => "image/x-portable-bitmap",
"pdf" => "application/pdf",
"pfx" => "application/x-pkcs12",
"pgm" => "image/x-portable-graymap",
"pko" => "application/ynd.ms-pkipko",
"pma" => "application/x-perfmon",
"pmc" => "application/x-perfmon",
"pml" => "application/x-perfmon",
"pmr" => "application/x-perfmon",
"pmw" => "application/x-perfmon",
"pnm" => "image/x-portable-anymap",
"pot," => "application/vnd.ms-powerpoint",
"ppm" => "image/x-portable-pixmap",
"pps" => "application/vnd.ms-powerpoint",
"ppt" => "application/vnd.ms-powerpoint",
"prf" => "application/pics-rules",
"ps" => "application/postscript",
"pub" => "application/x-mspublisher",
"qt" => "video/quicktime",
"ra" => "audio/x-pn-realaudio",
"ram" => "audio/x-pn-realaudio",
"ras" => "image/x-cmu-raster",
"rgb" => "image/x-rgb",
"rmi" => "audio/mid",
"roff" => "application/x-troff",
"rtf" => "application/rtf",
"rtx" => "text/richtext",
"scd" => "application/x-msschedule",
"sct" => "text/scriptlet",
"setpay" => "application/set-payment-initiation",
"setreg" => "application/set-registration-initiation",
"sh" => "application/x-sh",
"shar" => "application/x-shar",
"sit" => "application/x-stuffit",
"snd" => "audio/basic",
"spc" => "application/x-pkcs7-certificates",
"spl" => "application/futuresplash",
"src" => "application/x-wais-source",
"sst" => "application/vnd.ms-pkicertstore",
"stl" => "application/vnd.ms-pkistl",
"stm" => "text/html",
"svg" => "image/svg+xml",
"sv4cpio" => "application/x-sv4cpio",
"sv4crc" => "application/x-sv4crc",
"swf" => "application/x-shockwave-flash",
"t" => "application/x-troff",
"tar" => "application/x-tar",
"tcl" => "application/x-tcl",
"tex" => "application/x-tex",
"texi" => "application/x-texinfo",
"texinfo" => "application/x-texinfo",
"tgz" => "application/x-compressed",
"tif" => "image/tiff",
"tiff" => "image/tiff",
"tr" => "application/x-troff",
"trm" => "application/x-msterminal",
"tsv" => "text/tab-separated-values",
"txt" => "text/plain",
"uls" => "text/iuls",
"ustar" => "application/x-ustar",
"vcf" => "text/x-vcard",
"vrml" => "x-world/x-vrml",
"wav" => "audio/x-wav",
"wcm" => "application/vnd.ms-works",
"wdb" => "application/vnd.ms-works",
"wks" => "application/vnd.ms-works",
"wmf" => "application/x-msmetafile",
"wps" => "application/vnd.ms-works",
"wri" => "application/x-mswrite",
"wrl" => "x-world/x-vrml",
"wrz" => "x-world/x-vrml",
"xaf" => "x-world/x-vrml",
"xbm" => "image/x-xbitmap",
"xla" => "application/vnd.ms-excel",
"xlc" => "application/vnd.ms-excel",
"xlm" => "application/vnd.ms-excel",
"xls" => "application/vnd.ms-excel",
"xlt" => "application/vnd.ms-excel",
"xlw" => "application/vnd.ms-excel",
"xof" => "x-world/x-vrml",
"xpm" => "image/x-xpixmap",
"xwd" => "image/x-xwindowdump",
"z" => "application/x-compress",
"zip" => "application/zip",
);

35
core/data/okato.php Normal file
View file

@ -0,0 +1,35 @@
<?php
// В ОКАТО приняты следующие сокращения:
// http://www.consultant.ru/online/base/?req=doc;base=LAW;n=62484
$_okato = array (
"р" => "район",
"г" => "город",
"пгт" => "поселок городского типа",
"рп" => "рабочий поселок",
"кп" => "курортный поселок",
"к" => "кишлак",
"пс" => "поселковый совет",
"сс" => "сельсовет",
"смн" => "сомон",
"вл" => "волость",
"дп" => "дачный поселковый совет",
"п" => "поселок сельского типа",
"нп" => "населенный пункт",
"п. ст" => "поселок при станции",
"ж/д ст" => "железнодорожная станция",
"с" => "село",
"м" => "местечко",
"д" => "деревня",
"сл" => "слобода",
"ст" => "станция",
"ст-ца" => "станица",
"х" => "хутор",
"у" => "улус",
"рзд" => "разъезд",
"клх" => "колхоз",
"им" => "имени",
"свх" => "совхоз",
"зим" => "зимовье",
);

92
core/data/regions.php Normal file
View file

@ -0,0 +1,92 @@
<?php
global $Regions;
$Regions = array (
0 => '',
2 => 'Алтайский край',
3 => 'Амурская область',
4 => 'Архангельская область',
5 => 'Астраханская область',
6 => 'Белгородская область',
7 => 'Брянская область',
8 => 'Владимирская область',
9 => 'Волгоградская область',
10 => 'Вологодская область',
11 => 'Воронежская область',
12 => 'г. Москва',
13 => 'г. Санкт-Петербург',
14 => 'Еврейская автономная область',
84 => 'Забайкальский край',
15 => 'Ивановская область',
16 => 'Иркутская область',
17 => 'Кабардино-Балкарская Республика',
18 => 'Калининградская область',
19 => 'Калужская область',
20 => 'Камчатский край',
21 => 'Карачаево-Черкесская Республика',
22 => 'Кемеровская область',
23 => 'Кировская область',
28 => 'Костромская область',
29 => 'Краснодарский край',
30 => 'Красноярский край',
31 => 'Курганская область',
32 => 'Курская область',
33 => 'Ленинградская область',
34 => 'Липецкая область',
35 => 'Магаданская область',
36 => 'Московская область',
37 => 'Мурманская область',
38 => 'Ненецкий автономный округ',
39 => 'Нижегородская область',
40 => 'Новгородская область',
41 => 'Новосибирская область',
42 => 'Омская область',
43 => 'Оренбургская область',
44 => 'Орловская область',
45 => 'Пензенская область',
46 => 'Пермский край',
47 => 'Приморский край',
48 => 'Псковская область',
49 => 'Республика Адыгея',
50 => 'Республика Алтай',
51 => 'Республика Башкортостан',
52 => 'Республика Бурятия',
25 => 'Республика Дагестан',
26 => 'Республика Ингушетия',
53 => 'Республика Калмыкия',
54 => 'Республика Карелия',
55 => 'Республика Коми',
56 => 'Республика Марий Эл',
57 => 'Республика Мордовия',
58 => 'Республика Саха(Якутия)',
59 => 'Республика Северная Осетия-Алания',
60 => 'Республика Татарстан',
61 => 'Республика Тыва',
62 => 'Республика Хакасия',
63 => 'Ростовская область',
64 => 'Рязанская область',
65 => 'Самарская область',
66 => 'Саратовская область',
67 => 'Сахалинская область',
68 => 'Свердловская область',
69 => 'Смоленская область',
70 => 'Ставропольский край',
71 => 'Таймырский (Долгано-Ненецкий) автономный округ',
72 => 'Тамбовская область',
73 => 'Тверская область',
74 => 'Томская область',
75 => 'Тульская область',
76 => 'Тюменская область',
77 => 'Удмуртская Республика',
78 => 'Ульяновская область',
80 => 'Хабаровский край',
81 => 'Ханты-Мансийский автономный округ',
82 => 'Челябинская область',
83 => 'Чеченская Республика',
85 => 'Чувашская Республика',
86 => 'Чукотский автономный округ',
87 => 'Эвенкийский автономный округ',
88 => 'Ямало-Ненецкий автономный округ',
89 => 'Ярославская область'
);

35
core/data/states.php Normal file
View file

@ -0,0 +1,35 @@
<?php
$_states = array (
0 => array('title' => ''),
1 => array(
'title' => 'Центральный федеральный округ',
'short' => 'ЦФО',
'regions' => array(6, 7, 8, 11, 12, 15, 19, 28, 32, 34, 36, 44, 64, 69, 72, 73, 75, 89)),
2 => array(
'title' => 'Южный федеральный округ',
'short' => 'ЮФО',
'regions' => array(5, 9, 17, 21, 29, 49, 25, 26, 53, 59, 63, 70, 83)),
3 => array(
'title' => 'Северо-западный федеральный округ',
'short' => 'СЗФО',
'regions' => array(4, 10, 13, 18, 33, 37, 38, 40, 48, 54, 55)),
4 => array(
'title' => 'Дальневосточный федеральный округ',
'short' => 'ДФО',
'regions' => array(3, 14, 20, 35, 47, 58, 67, 80, 86)),
5 => array(
'title' => 'Сибирский федеральный округ',
'short' => 'СФО',
'regions' => array(2, 16, 22, 30, 41, 42, 50, 52, 61, 62, 71, 74, 84, 86, 87)),
6 => array(
'title' => 'Уральский федеральный округ',
'short' => 'УФО',
'regions' => array(31, 68, 76, 81, 82, 88)),
7 => array(
'title' => 'Приволжский федеральный округ',
'short' => 'ПФО',
'regions' => array(23, 39, 43, 45, 46, 51, 56, 57, 60, 65, 66, 77, 78, 85)));
$States = &$_states;

18
core/database.php Normal file
View file

@ -0,0 +1,18 @@
<?php
/**
* Ïðîñòîé êëàññ(Factory) äëÿ ðàáîòû ñ áàçàìè äàííûõ
*/
class Database
{
static function getConnection (array $dsn)
{
require_once "core/drivers/database." . strtolower($dsn['phptype']) . ".php";
$name = "Database_" . strtoupper($dsn['phptype']);
$database = new $name();
$database->connect($dsn);
return $database;
}
}
?>

305
core/database_pdo.php Normal file
View file

@ -0,0 +1,305 @@
<?php
/**
* Êëàññ îáîëî÷êà äëÿ PDO äëÿ çàìåíû Creole
*/
class Database extends PDO
{
public function __construct($dsn, $username = false, $password = false)
{
parent::__construct($dsn, $username, $password);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDODatabaseStatement', array()));
}
public function getDSN()
{
return $this->dsn;
}
static function getConnection(array $dsn)
{
if ($dsn['phptype'] == 'pgsql') {
$port = (isset($dsn['port'])) ? "port={$dsn['port']};" : "";
$connection = new Database("{$dsn['phptype']}:host={$dsn['hostspec']}; $port dbname={$dsn['database']}", $dsn['username'], $dsn['password']);
}
if ($dsn['phptype'] == 'sqlite') {
$connection = new Database("{$dsn['phptype']}:{$dsn['database']}");
}
$connection->dsn = $dsn;
return $connection;
}
public function executeQuery($query)
{
$sth = $this->prepare($query);
$sth->setFetchMode(PDO::FETCH_ASSOC);
$sth->execute();
// print_r($sth->fetchAll());
return $sth;//$sth->fetchAll();
}
public function prepareStatement($query)
{
return new DatabaseStatement($query, $this);
}
// Äëÿ ñîâìåñòèìîñòè ñî ñòàðûì ïðåäñòàâëåíèåì áàç äàííûõ CIS
public function fetchAllArray($query)
{
$sth = $this->prepare($query);
$sth->setFetchMode(PDO::FETCH_ASSOC);
$sth->execute();
return $sth->fetchAll();
}
public function fetchOneArray($query)
{
$sth = $this->prepare($query);
$sth->setFetchMode(PDO::FETCH_ASSOC);
$sth->execute();
return $sth->fetch();
}
private function assignQuote($x, $y)
{
return $x . "=" . $this->quote($y);
}
function insertQuery($table, array $values)
{
return $this->query("INSERT INTO $table (" . implode(",", array_keys($values))
. ") VALUES (" . implode(",", array_map(array($this, 'quote'), array_values($values))) . ")");
}
function updateQuery($table, array $values, $cond)
{
return $this->query("UPDATE $table SET " . implode(",",
array_map(array($this, 'assignQuote'), array_keys($values), array_values($values))) . " WHERE $cond");
}
function getNextId($seq)
{
$result = $this->fetchOneArray("SELECT nextval('$seq')");
return $result['nextval'];
}
function close()
{
return null;
}
}
class PDODatabaseStatementIterator implements Iterator
{
private $result;
private $pos = 0;
private $fetchmode;
private $row_count;
private $rs;
/**
* Construct the iterator.
* @param PgSQLResultSet $rs
*/
public function __construct($rs)
{
$this->result = $rs;
$this->row_count = $rs->getRecordCount();
}
function rewind()
{
// echo 'rewind';
$this->pos = 0;
}
function valid()
{
return ( $this->pos < $this->row_count );
}
function key()
{
return $this->pos;
}
function current()
{
return $this->result->fetch(PDO::FETCH_ASSOC);
}
function next()
{
$this->pos++;
}
function seek ( $index )
{
$this->pos = $index;
}
function count ( ) {
return $this->row_count;
}
}
class PDODatabaseStatement extends PDOStatement implements IteratorAggregate
{
protected $cursorPos = 0;
function getIterator()
{
// echo 'getiterator';
return new PDODatabaseStatementIterator($this);
}
protected function __construct() {
}
function rewind()
{
// echo 'rewind';
$this->cursorPos = 0;
}
public function seek($rownum)
{
// echo 'seek';
if ($rownum < 0) {
return false;
}
// PostgreSQL rows start w/ 0, but this works, because we are
// looking to move the position _before_ the next desired position
$this->cursorPos = $rownum;
return true;
}
function valid()
{
// echo 'valid';
return ( true );
}
public function first()
{
// echo 'first';
if($this->cursorPos !== 0) { $this->seek(0); }
return $this->next();
}
function next()
{
// echo 'next';
if ($this->getRecordCount() > $this->cursorPos) {
$this->fields = $this->fetch(PDO::FETCH_ASSOC);
} else {
$this->fields = null;
}
// $this->cursorPos++;
return true;
}
function key() {
// echo 'key';
return $this->cursorPos;
}
function current()
{
// echo 'current';
return $this->result->fetch(PDO::FETCH_ASSOC);
}
function getRow()
{
return $this->fields;
}
function getInt($name)
{
return intval($this->fields[$name]);
}
function getBlob($name)
{
return $this->fields[$name];
}
function getString($name)
{
return $this->fields[$name];
}
function get($name)
{
return $this->cursorPos[$name];
}
function getRecordCount()
{
return $this->rowCount();
}
}
/**
* Êëàññ îáîëî÷êà äëÿ PDOStatement äëÿ çàìåíû Creole
*/
class DatabaseStatement
{
protected $limit = null;
protected $offset = null;
protected $statement = null;
protected $binds = array();
protected $conn;
protected $query;
function __construct($query, $conn) {
$this->query = $query;
$this->conn = $conn;
}
function setInt($n, $value)
{
$this->binds [] = array($n, $value, PDO::PARAM_INT);
}
function setString($n, $value)
{
$this->binds [] = array($n, $value, PDO::PARAM_STR);
}
function setBlob($n, $value)
{
$this->binds [] = array($n, $value, PDO::PARAM_BLOB);
}
function setLimit($limit)
{
$this->limit = $limit;
}
function setOffset($offset)
{
$this->offset = $offset;
}
function executeQuery()
{
if ($this->limit) {
$this->queryString .= " LIMIT {$this->limit} OFFSET {$this->offset}";
}
$stmt = $this->conn->prepare($this->query);
foreach ($this->binds as $bind) {
list($n, $value, $type) = $bind;
$stmt->bindValue($n, $value, $type);
}
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->execute();
return $stmt;
}
}

View file

@ -0,0 +1,50 @@
<?php
require_once 'core/drivers/db.php';
/**
* Ïðîñòîé êëàññ äëÿ ðàáîòû ñ áàçàìè äàííûõ
*/
class Database_MYSQL extends DB implements IDatabase
{
public function connect(array $dsn)
{
$db = @mysql_pconnect($dsn['hostspec'], $dsn['username'], $dsn['password'])
or die("Unable connect to database!");
mysql_select_db($dsn['database']);
return ($this->db = $db);
}
public function close()
{
return mysql_close($this->db);
}
public function query($query)
{
$res = mysql_query($this->db, $query)
or die("Error: wrong SQL query #$query#");
return $res;
}
public function fetchAllArray($query)
{
$res = $this->query($query);
while ($row = mysql_fetch_array ($res))
$rows[] = $row;
mysql_free_result($res);
return ($rows) ? $rows : array();
}
public function fetchOneArray($query)
{
$res = $this->query($query);
$row = mysql_fetch_array($res);
mysql_free_result($res);
return ($row) ? $row : array();
}
}
?>

View file

@ -0,0 +1,54 @@
<?php
require_once 'core/drivers/db.php';
/**
* Ïðîñòîé êëàññ äëÿ ðàáîòû ñ áàçàìè äàííûõ
*/
class Database_ODBC extends DB implements IDatabase
{
public function connect(array $dsn)
{
$db = @odbc_connect($dsn['database'], $dsn['username'], $dsn['password'])
or die("Unable connect to database!");
return ($this->db = $db);
}
public function close()
{
return odbc_close($this->db);
}
public function query($query)
{
$res = odbc_exec($this->db, $query)
or die("Error: wrong SQL query #$query#");
return $res;
}
public function fetchAllArray($query)
{
$res = $this->query($query);
$to = odbc_num_fields($res);
while (odbc_fetch_row($res)) {
for ($i = 1; $i <= $to; $i++) {
$row [odbc_field_name($res, $i)] = trim(odbc_result($res, $i));
}
$rows[] = $row;
}
return ($rows)? $rows : array();
}
public function fetchOneArray($query)
{
$res = $this->query($query);
if (!odbc_fetch_row($res)) return array ();
$to = odbc_num_fields($res);
for ($i = 1; $i <= $to; $i++) {
$row [odbc_field_name($res, $i)] = trim(odbc_result($res, $i));
}
return $row;
}
}
?>

View file

@ -0,0 +1,74 @@
<?php
require_once 'core/drivers/db.php';
/**
* Ïðîñòîé êëàññ äëÿ ðàáîòû ñ áàçàìè äàííûõ
*/
class Database_PGSQL extends DB implements IDatabase
{
public function connect(array $dsn)
{
if (isset($dsn['port'])) {
$port = "port={$dsn['port']}";
} else {
$port = "port=5432";
}
$str = "host={$dsn['hostspec']} $port dbname={$dsn['database']} user={$dsn['username']} password={$dsn['password']}";
$db = @pg_connect($str)
or die("Unable connect to database!");
return ($this->db = $db);
}
public function close()
{
return pg_close($this->db);
}
public function query($query)
{
$res = pg_query($this->db, $query)
or die("Error: wrong SQL query #$query#");
return $res;
}
public function fetchAllArray($query, $type = PGSQL_ASSOC)
{
$res = $this->query($query);
$rows = array();
while ($row = pg_fetch_array($res, NULL, $type)) {
$rows[] = $this->clean($row);
}
pg_free_result($res);
return ($rows) ? $rows : array();
}
public function affectedRows()
{
return pg_affected_rows($this->db);
}
private function clean($row)
{
foreach ($row as $key => $value) {
$row[$key] = trim($value);
}
return $row;
}
public function fetchOneArray($query, $type = PGSQL_ASSOC)
{
$res = $this->query($query);
$row = pg_fetch_array($res, NULL, $type);
pg_free_result($res);
return ($row) ? $this->clean($row) : array();
}
function getNextId($seq)
{
$result = $this->fetchOneArray("SELECT nextval('$seq')");
return $result['nextval'];
}
}

50
core/drivers/db.php Normal file
View file

@ -0,0 +1,50 @@
<?php
/**
* Èíòåðôåéñ äðàéâåðà êëàññà áàç äàííûõ
*/
interface IDatabase
{
public function connect(array $dsn);
public function close();
public function query($query);
public function fetchAllArray($query);
public function fetchOneArray($query);
}
abstract class DB implements IDatabase
{
const limit = 1024;
protected $db;
public static function quote($x)
{
return "'" . $x . "'";
}
private static function assign_quote($x, $y)
{
return $x . "='" . $y . "'";
}
function insert($table, array $values)
{
return $this->query("INSERT INTO $table (" . implode(",", array_keys($values))
. ") VALUES (" . implode(",", array_map(array('self', 'quote'), array_values($values))) . ")");
}
function update($table, array $values, $cond)
{
return $this->query("UPDATE $table SET " . implode(",",
array_map(array('self', 'assign_quote'), array_keys($values), array_values($values))) . " WHERE $cond");
}
function check_text($text)
{
if(strlen($text) > self::limit) $text = substr($text, 0, self::limit);
$text = htmlspecialchars(trim($text));
return $text;
}
}
?>

86
core/error.php Normal file
View file

@ -0,0 +1,86 @@
<?php
// Report simple running errors
define('ERROR_FILE', 'phedor@edu.yar.ru');
define('ERROR_TYPE', 1);
ini_set('error_reporting', 0);
ini_set('display_errors', 0);
error_reporting(0);
function get_error_name($id) {
$names = array(1 => 'E_ERROR',
2 => 'E_WARNING',
4 => 'E_PARSE',
8 => 'E_NOTICE',
16 => 'E_CORE_ERROR',
32 => 'E_CORE_WARNING',
64 => 'E_COMPILE_ERROR',
128 => 'E_COMPILE_WARNING',
256 => 'E_USER_ERROR',
512 => 'E_USER_WARNING',
1024 => 'E_USER_NOTICE',
2048 => 'E_STRICT',
4096 => 'E_RECOVERABLE_ERROR',
8192 => 'E_DEPRECATED',
16384 => 'E_USER_DEPRECATED',
30719 => 'E_ALL');
return $names[$id];
}
function send_error($description) {
$message = "request: " . "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] . "\n";
$message .= "from: " . $_SERVER['REMOTE_ADDR'] . "\n";
if(isset($_SERVER['HTTP_REFERER'])) $message .= "referer: " . $_SERVER['HTTP_REFERER'] . "\n";
$message .= "post:" . var_export($_POST, true) . "\n";
$message .= "description: " . $description;
// error_log($message, ERROR_TYPE, ERROR_FILE);
return false;
}
function error_handler($errno, $errstr, $errfile, $errline) {
$message = "request: " . "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] . "\n";
$message .= "from: " . $_SERVER['REMOTE_ADDR'] . "\n";
if(isset($_SERVER['HTTP_REFERER'])) $message .= "referer: " . $_SERVER['HTTP_REFERER'] . "\n";
$message .= "post:" . var_export($_POST, true) . "\n";
$message .= "type: " . get_error_name($errno) . "\n"
. "error: " . $errstr . "\n"
. "line: " . $errline . "\n"
. "file: " . $errfile;
// error_log($message, ERROR_TYPE, ERROR_FILE);
return false;
}
function shutdown() {
$error = error_get_last();
$message = "request: " . "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] . "\n";
$message .= "from: " . $_SERVER['REMOTE_ADDR'] . "\n";
if(isset($_SERVER['HTTP_REFERER'])) $message .= "referer: " . $_SERVER['HTTP_REFERER'] . "\n";
if (is_array($error)) {
foreach ($error as $info => $string) {
$message .= "{$info}: {$string}\n";
}
}
error_log($message, ERROR_TYPE, ERROR_FILE);
}
function exception_handler($exception) {
$message = "request: " . "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] . "\n";
$message .= "from: " . $_SERVER['REMOTE_ADDR'] . "\n";
if(isset($_SERVER['HTTP_REFERER'])) $message .= "referer: " . $_SERVER['HTTP_REFERER'] . "\n";
$message .=
"file: " . $exception->getFile() . "\n"
. "line: " . $exception->getLine() . "\n"
. "message: " . $exception->getMessage() . "\n"
. "trace: " . $exception->getTraceAsString();
error_log($message, ERROR_TYPE, ERROR_FILE);
return true;
}
set_exception_handler('exception_handler');
set_error_handler("error_handler");
//register_shutdown_function('shutdown');

82
core/file.php Normal file
View file

@ -0,0 +1,82 @@
<?php
require_once 'core/sort.php';
//Ñòàíîâèòüñÿ ïîõîæå íà ðàáîòó ôàéëîâ ÷åðåç SPL âîçìîæíî ñòîèò ðåàëèçîâàòü íà áàçå åãî
class FileRecord
{
public $file;
protected $src;
protected $parent;
function __construct(array $file, $src, $parent = false)
{
$this->file = $file;
$this->filename = $src;
$this->parent = $parent;
}
function get($name) {
return isset($this->file[$name]) ? $this->file[$name] : null;
}
function fileStat()
{
$type = is_dir($this->filename);
return array(
'name' => ($this->parent) ? ".." : $this->getName(),
'type' => $type,
'extension' => ($type) ? 'folder' : pathinfo($this->filename, PATHINFO_EXTENSION),
'date' => date("d.m.Y H:i", $this->getTime()),
'access' => 0,
'size' => ($type) ? "" : $this->getSizeString(),
'state' => isset($this->file['state']) ? $this->file['state'] : 'unknown',
'title' => $this->getTitle(),
/*'author' => $this->file['author'],
'description' => $this->file['description'],
'keywords' => $this->file['keywords'],*/
);
}
function isExpected()
{
if (isset($this->file['state'])) {
return ($this->file['state'] == 'expected');
}
return false;
}
function getSizeString()
{
$size = $this->getSize();
foreach (array('á ', 'Ká', 'Má') as $suffix) {
if (($size / 1024) <= 1) {
return round($size, 0) . ' ' . $suffix;
}
$size /= 1024;
}
return round($size, 0) . ' GB';
}
function getSize()
{
return ($this->isExpected()) ? 0 : filesize($this->filename);
}
function getTime()
{
return ($this->isExpected()) ? 0 : filemtime($this->filename);
}
function getName()
{
return pathinfo($this->filename, PATHINFO_BASENAME);
}
function getTitle()
{
return isset($this->file['title']) ? $this->file['title'] : $this->getName();
}
}
?>

583
core/filesystem.php Normal file
View file

@ -0,0 +1,583 @@
<?php
require_once 'core/path.php';
require_once 'core/file.php';
interface IFileSystem
{
// Îïåðàöèè íàä ôàéëàìè
public function makeDirectory($name);
public function deleteDirectory($name);
public function deleteFile($name);
public function renameFile($source, $destination);
public function copyFile($source, $destination);
public function moveUploadedFile($source, $destination);
// deleteDirectoryRecursive
public function isDir($name);
public function readFile($source);
public function writeFile($source, $content);
// Ñîäåðæàíèå äèðåêòîðèè
public function directoryFiles($name);
public function directoryFilesRecursive($name);
}
interface IFileControl
{
public function commitFile($name, $who, $message);
public function readFileVersion($name, $version = false);
// Èíôîðìàöèÿ î ôàéëå
public function getFileLog($name);
public function getFileInfo($name);
}
// Ðåàëüíàÿ ôàéëîâàÿ ñèñòåìà
class FileSystem implements IFileSystem
{
protected $hidden = array('.', '..');
protected $visible = null;
public function __construct()
{
}
public function setVisibleFiles(array $visible)
{
$this->visible = $visible;
}
public function setHiddenFiles(array $hidden)
{
$this->hidden = array_merge($this->hidden, $hidden);
}
/**
*
*/
public function makeDirectory($name)
{
if (file_exists($name) === false) {
mkdir($name);
}
}
/**
*
*/
public function makeFile($name)
{
if (file_exists($name) === false) {
file_put_contents($name, '');
}
}
/**
*
*/
public function deleteDirectory($name)
{
rmdir($name);
}
/**
*
*/
public function deleteDirectoryRecursive($name)
{
if ($handle = opendir($name)) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
$sf = $name . DIRECTORY_SEPARATOR . $file;
if (is_dir($sf) && !is_link($sf)) {
self::deleteDirectoryRecursive($sf);
} else {
unlink($sf);
}
}
}
closedir($handle);
@rmdir($name);
}
}
/**
*
*/
public function deleteFile($name)
{
if (file_exists($name)) {
unlink($name);
}
}
// Ïðè ïåðåìåùåíèè èëè âñå ôàéëû åñëè åñòü ñîâïàäåíèÿ ïåðåïèñûâàþòñÿ
/**
*
*/
public function renameFile($source, $destination)
{
rename($source, $destination);
}
/**
*
*/
public function copyFile($source, $destination)
{
copy($source, $destination);
}
/**
*
*/
public function copyDirectory($source, $destination)
{
if (is_dir($source)) {
if (! file_exists($destination)) mkdir($destination);
$handle = opendir($source);
while (false !== ($file = readdir($handle))) {
$entry = $source . DIRECTORY_SEPARATOR . $file;
if (is_dir($entry)) {
self::copyDirectory($entry, $destination . DIRECTORY_SEPARATOR . $file);
} else {
copy($entry, $destination . DIRECTORY_SEPARATOR . $file);
}
}
}
}
/**
*
*/
public function moveUploadedFile($source, $destination)
{
move_uploaded_file($source, $destination);
}
/**
*
*/
public function isVisible($file)
{
if (in_array(basename($file), $this->hidden) === true) {
return false;
}
return ($this->isDir($file) || $this->visible == null) || in_array(pathinfo($file, PATHINFO_EXTENSION), $this->visible);
}
/**
*
*/
public function directoryFiles($name)
{
$result = array();
$files = scandir($name);
foreach ($files as $file) {
$fullname = $name . DIRECTORY_SEPARATOR . $file;
if ($this->isVisible($fullname)) {
$result [$file] = new FileRecord(array(), $fullname);
}
}
return $result;
}
/**
*
*/
public function readFile($name)
{
return file_get_contents($name);
}
/**
*
*/
public function writeFile($name, $content)
{
file_put_contents($name, $content);
}
/**
*
*/
public function directoryFilesRecursive($name)
{
}
function isDir($name)
{
return is_dir($name);
}
}
// Òî ÷òî õðàíèòñÿ â áàçå äàííûõ
class EFileSystem implements IFileSystem, IFileControl
{
protected $basepath;
protected $db;
public function __construct($basepath, $db, $fs)
{
$this->basepath = $basepath;
$this->db = $db;
$this->fs = $fs;
}
/*function createExtendRecord($index)
{
static $fileSQL = "INSERT INTO file (id_record) VALUES (?)";
$query = $this->db->prepareStatement($fileSQL);
$query->setString(1, $index);
$query->executeQuery();
}*/
private function createRecord($name, $type, $path)
{
static $recordSQL = "INSERT INTO files (filename, idfile, lastrevdate, filepath, filetype) VALUES (?, ?, ?, ?, ?)";
$last = $this->db->getIdGenerator();
$index = $last->getId('files_idfile_seq');
$query = $this->db->prepareStatement($recordSQL);
$query->setString(1, $name);
$query->setInt(2, $index);
$query->setInt(3, 0);
$query->setString(4, $path);
$query->setString(5, $type);
$query->executeQuery();
/*if ($type == 0) {
$this->createExtendRecord($index);
}*/
return $index;
}
function setVisibleFiles(array $visible)
{
$this->fs->setVisibleFiles($visible);
}
function setHiddenFiles(array $hidden)
{
$this->fs->setHiddenFiles($hidden);
}
public function getFullPath($name)
{
return Path::join($this->basepath, $name);
}
private function getRecordId($name, $path)
{
static $recordSQL = "SELECT idfile FROM files WHERE filename = ? AND filepath = ?";
$query = $this->db->prepareStatement($recordSQL);
$query->setString(1, $name);
$query->setString(2, $path);
$result = $query->executeQuery();
if ($result->next()) {
$index = $result->getInt('idfile');
return $index;
}
return false; // Ìîæåò ëó÷øå êèäàòü èñêëþ÷åíèå ??
}
function getIdFromPath($name)
{
return $this->getRecordId(basename($name), self::getPathName($name));
}
// Ñîçäàíèå íîâîé äèðåêòîðèè
public function makeDirectory($name)
{
$path = new Path($name);
$fullpath = $this->basepath;
$temp_path = '';
foreach ($path->getParts() as $subpath)
{
$index = $this->getRecordId($subpath, $temp_path);
if ($index === false) {
$index = $this->createRecord($subpath, 1, $temp_path);
}
$temp_path = Path::join($temp_path, $subpath);
}
$this->fs->makeDirectory($this->getFullPath($name));
}
public function isDir($name)
{
return $this->fs->isDir($this->getFullPath($name));
}
// Ïåðåèìåíîâàíèå ôàéëà èëè äèðåêòîðèè âñå èçìåíåíèÿ äîëæíû çàïèñûâàòüñÿ â áàçó ÷òîáû ìîæíî áûëî ñäåëàòü îòìåíó !!!
public function renameFile($source, $destination)
{
// Ïðè ïåðåìåùåíèè ôàéëû ìîãóò ñîâïàäàòü
$stmt = $this->db->prepareStatement('UPDATE files SET filepath = ?, filename = ? WHERE filepath = ? AND filename = ?');
$stmt->setString(1, self::getPathName($destination));
$stmt->setString(2, basename($destination));
$stmt->setString(3, self::getPathName($source));
$stmt->setString(4, basename($source));
$stmt->executeQuery();
if ($this->isDir($source)) {
$length = strlen($from) + 1;
$stmt = $this->db->prepareStatement("UPDATE file
SET filepath = '?' || substr(filepath, ?) WHERE filepath LIKE (?) OR filepath LIKE (? || '/%')");
$stmt->setString(1, $destination);
$stmt->setInt(2, $length);
$stmt->setString(3, $source);
$stmt->setString(4, $source);
}
$this->fs->renameFile($this->getFullPath($source), $this->getFullPath($destination));
}
// Êîïèðîâàíèå ôàéëà èëè äèðåêòîðèè
public function copyFile($source, $destination)
{
// Ïðè êîïèðîâàíèè ôàéëû ìîãóò ñîâïàäàòü
$stmt = $this->db->prepareStatement('INSERT INTO files (filepath, filename, lastrevdate) VALUES (?, ?, ?)');
$stmt->setString(1, self::getPathName($destination));
$stmt->setString(2, basename($destination));
$stmt->setString(3, time());
$stmt->executeQuery();
if ($this->isDir($source)) {
$stmt = $this->db->prepareStatement("INSERT INTO files (filepath, filename, lastrevdate)
SELECT '?' || substr(filepath, ?) AS filepath, filename, lastrevdate WHERE WHERE filepath LIKE (?) OR filepath LIKE (? || '/%')");
$stmt->setString(1, $destination);
$stmt->setInt(2, $length);
$stmt->setString(3, $source);
$stmt->setString(4, $source);
}
$this->fs->copyFile($this->getFullPath($source), $this->getFullPath($destination));
}
private function getPathName($name)
{
$path = dirname($name);
return ($path == '.') ? '' : $path;
}
public function makeFile($name)
{
$base = self::getPathName($name);
$this->makeDirectory($base);
$filename = basename($name);
$index = $this->getRecordId($filename, $base);
if ($index === false) {
$index = $this->createRecord($filename, 0, $base);
}
$this->fs->makeFile($this->getFullPath($name));
}
public function readFile($name)
{
return $this->fs->readFile($this->getFullPath($name));
}
public function readFileVersion($name, $revision = false)
{
if ($revision === false) {
return $this->readFile($name);
} else {
$id_file = $this->getIdFromPath($name);
$query = $this->db->prepareStatement("SELECT * FROM history WHERE revision = ? AND idfile = ?");
$query->setInt(1, $revision);
$query->setInt(2, $id_file);
$file = $query->executeQuery();
if ($file->next()) {
return gzuncompress($file->getBlob('content'));
}
}
return null;
}
public function writeFile($name, $content)
{
$this->makeFile($name);
$this->fs->writeFile($this->getFullPath($name), $content);
}
public function getLastRevision($name)
{
$id_file = $this->getIdFromPath($name);
$stmt = $this->db->prepareStatement("SELECT * FROM history WHERE revision IN (SELECT MAX(revision) AS lastrev FROM history WHERE idfile = ?)");
$stmt->setInt(1, $id_file);
$rev = $stmt->executeQuery();
if ($rev->next()) {
return $rev;
}
return false;
}
/**
*
*/
public function commitFile($name, $owner, $message)
{
$id_file = $this->getIdFromPath($name);
$content = $this->readFile($name);
$stmt = $this->db->prepareStatement("SELECT MAX(revision) AS lastrev FROM history WHERE idfile = ?");
$stmt->setInt(1, $id_file);
$rev = $stmt->executeQuery();
$revision = ($rev->next()) ? $rev->getInt('lastrev') + 1 : 1;
$query = $this->db->prepareStatement("INSERT INTO history (content, owner, revsummary, revdate, revision, idfile) VALUES (?, ?, ?, ?, ?, ?)");
$query->setBlob(1, gzcompress($content));
$query->setString(2, $owner);
$query->setString(3, $message);
$query->setInt(4, time());
$query->setInt(5, $revision);
$query->setInt(6, $id_file);
$query->executeQuery();
}
/**
*
*/
public function getFileDifference($name, $revision1, $revision2 = false)
{
$first = $this->readFileVersion($name, $revision1);
$second = $this->readFileVersion($name, $revision2);
}
/**
*
*/
public function getFileLog($name)
{
$id_file = $this->getIdFromPath($name);
$query = $this->db->prepareStatement("SELECT revision,revsummary,owner,revdate FROM history WHERE idfile = ? ORDER BY revision");
$query->setInt(1, $id_file);
$list = $query->executeQuery();
return iterator_to_array($list->getIterator());
}
public function directoryFiles($name)
{
$result = $this->fs->directoryFiles($this->getFullPath($name));
/* Ñïèñîê ôàéëîâ èç áàçû äàííûõ */
$query = $this->db->prepareStatement("SELECT * FROM files WHERE filepath = ?");
$query->setString(1, $name);
$list = $query->executeQuery();
foreach ($list as $file) {
$fullpath = $this->getFullPath($name . DIRECTORY_SEPARATOR . $file['filename']);
if ($this->fs->isVisible($fullpath)) {
$file['state'] =
((isset($result[$file['filename']])) ?
(($file['lastrevdate'] > $file['change']) ? 'exclamation' : 'unchanged')
: 'expected');
$record = new FileRecord($file, $fullpath);
$result [$file['filename']] = $record;
}
}
return $result;
}
public function getFileInfo($name)
{
$index = $this->getIdFromPath($name);
$fullpath = $this->basepath . DIRECTORY_SEPARATOR . $name;
if ($index !== false) {
$query = $this->db->prepareStatement("SELECT * FROM files AS r LEFT JOIN filemeta AS f ON r.idfile = f.id_record WHERE r.idfile = ?");
$query->setInt(1, $index);
$list = $query->executeQuery();
$list->next();
$file = $list->getRow();
$file['state'] = (file_exists($fullpath) ? 'unchanged' : 'expected');
$result = new FileRecord($file, $fullpath);
} else {
$result = new FileRecord(array(), $fullpath);
}
return $result;
}
public function setFileInfo($name, Collection $list)
{
$index = $this->getIdFromPath($name);
if ($index !== false) {
$stmt = $this->db->prepareStatement("UPDATE files SET title = ? WHERE idfile = ?");
$stmt->setString(1, $list->get('title'));
$stmt->setInt(2, $index);
$stmt->executeQuery();
/*if (some($list, array('keywords', 'author', 'description'))) {
$hasfile = $this->db->executeQuery("SELECT * FROM file WHERE id_record = $index");
if(!$hasfile->next()) {
static $fileSQL = "INSERT INTO file (id_record) VALUES (?)";
$query = $this->db->prepareStatement($fileSQL);
$query->setString(1, $index);
$query->executeQuery();
}
$query = $this->db->prepareStatement("UPDATE file SET keywords = ?, author = ?, description = ? WHERE id_record = ?");
$query->setString(1, $list->get('keywords'));
$query->setString(2, $list->get('author'));
$query->setString(3, $list->get('description'));
$query->setInt(4, $index);
$query->executeQuery();
}*/
}
}
/**
* Óäàëÿåì äèðåêòîðèþ åñëè îíà íå ïóñòàÿ
*/
function deleteDirectory($name)
{
$index = $this->getIdFromPath($name);
$query = $this->db->prepareStatement("SELECT COUNT(*) AS col FROM files WHERE filepath = (?) OR filepath LIKE(? || '/%')");
$query->setString(1, $name);
$query->setString(2, $name);
$result = $query->executeQuery();
$result->next();
if ($index && $result->getInt('col') == 0) {
$query = $this->db->prepareStatement("DELETE FROM files WHERE idfile = ?");
$query->setInt(1, $index);
$query->executeQuery();
}
$this->fs->deleteDirectory($this->getFullPath($name));
}
function deleteFile($name)
{
$index = $this->getIdFromPath($name);
if ($index) {
$query = $this->db->prepareStatement("DELETE FROM history WHERE idfile = ?;
DELETE FROM filemeta WHERE id_record = ?;DELETE FROM files WHERE idfile = ?");
$query->setInt(1, $index);
$query->setInt(2, $index);
$query->setInt(3, $index);
$query->executeQuery();
}
$this->fs->deleteFile($this->getFullPath($name));
}
function moveUploadedFile($source, $destination)
{
$this->fs->moveUploadedFile($source, $this->getFullPath($destination));
$this->makeFile($destination);
}
function directoryFilesRecursive($name)
{
$files = $this->fs->directoryFilesRecursive($this->getFullPath($name));
$query = $this->db->prepareStatement("DELETE FROM files WHERE filepath = (?) OR filepath LIKE (? || '/%')");
$query->setString(1, $name);
$query->setString(2, $name);
$query->executeQuery();
}
}

View file

@ -0,0 +1,34 @@
<?php
/**
* Ôèëüòð äåéñòâèé
*/
class ActionAccess
{
public $access = array();
function __construct($processor)
{
$this->processor = $processor;
}
/**
* Ïðîâåðêà äîñòóïíûõ äåéñòâèé äëÿ ïîëüçîâàòåëÿ
* !! Ðåàëèçàöèÿ êëàññà ïðîâåðêè äåéñòâèé íå äîëæíà áûòü âíóòðè Êîíòðîëëåðà!!!
* Èíôîðìàöèÿ î äîñòóïå ìîæåò áûòü â ôàéëå, áàçå äàííûõ è ò.ä.
*/
function checkAction($action)
{
// Èìïëèêàöèÿ !! http://ru.wikipedia.org/wiki/Èìïëèêàöèÿ
return (!isset($this->access[$action]) || in_array(UserAccess::$access, $this->access[$action]));
}
function execute(HTTPRequest $request)
{
$action = $request->getAction();
if(! $this->checkAction($action)) {
$request->set('action', 'index');
}
return $this->processor->execute($request);
}
}

View file

@ -0,0 +1,26 @@
<?php
require_once 'core/path.php';
class ActionLogger
{
public $before = array ();
public $file;
public $action;
public $processor;
function __construct($processor)
{
$this->processor = $processor;
$this->file = fopen(Shortcut::getUrl('access.log'), "a");
}
function execute(HTTPRequest $request)
{
$action = $request->getAction();
if(in_array($action, $this->before)) {
fwrite($this->file, "time: " . date("r", time()) . " query: ". json::encode(array_merge($_POST, $_GET)) . " by: " . UserAccess::$name . "\n");
}
return $this->processor->execute($request);
}
}

72
core/filter/filter.php Normal file
View file

@ -0,0 +1,72 @@
<?php
require_once 'filterbase.php';
require_once 'filterlogin.php';
// Êëàññ äîëæåí áûòü â áèáëèîòåêå ïðèëîæåíèÿ
class UserAccess
{
const LIFE_TIME = 1800; // = 30min * 60sec;
static $fullname;
static $name;
static $access;
static $password;
static $id;
static $db;
protected function __construct()
{
}
public static function setUp($db)
{
self::$db = $db;
}
public static function getUserByQuery($stmt)
{
global $GROUPS;
$result = $stmt->executeQuery();
if ($result->next()) {
self::$access = $GROUPS[$result->getString('access')];
self::$name = $result->getString('login');
self::$id = $result->getInt('id_user');
self::$password = $result->getString('password');
self::$fullname = implode(' ', array(
$result->getString('surname'),
$result->getString('firstname'),
$result->getString('patronymic')));
return $result;
}
return null;
}
public static function getUserByLogin($login)
{
$stmt = self::$db->prepareStatement("SELECT * FROM users WHERE login = ?");
$stmt->setString(1, $login);
$result = self::getUserByQuery($stmt);
if ($result) {
$time = time();
$id = self::$id;
self::$db->executeQuery("UPDATE users SET lasttime = $time WHERE id_user = $id"); // Âðåìÿ âõîäà
}
return $result;
}
public static function getUserById($id)
{
$stmt = self::$db->prepareStatement("SELECT * FROM users WHERE id_user = ?");
$stmt->setInt(1, $_SESSION ['access']);
$result = self::getUserByQuery($stmt);
if ($result) {
$lasttime = $result->getInt('lasttime');
$time = time();
if ($time - $lasttime > self::LIFE_TIME) return null; // Âûøëî âðåìÿ ñåññèè
$id = self::$id;
$stmt = self::$db->executeQuery("UPDATE users SET lasttime = $time WHERE id_user = $id"); // Âðåìÿ ïîñëåäíåãî îáðàùåíèÿ âõîäà
}
return $result;
}
}

View file

@ -0,0 +1,28 @@
<?php
/**
* Ïîïûòêà ðåàëèçîâàòü ôèëüòð äëÿ çàïðîñîâ
*/
class Filter
{
public $processor;
public function __construct($processor)
{
$this->processor = $processor;
}
public function execute(Collection $request)
{
return $this->processor->execute($request);
}
public function getView($name)
{
return $this->processor->getView($name);
}
public function getConnection()
{
return $this->processor->getConnection();
}
}

105
core/filter/filterlogin.php Normal file
View file

@ -0,0 +1,105 @@
<?php
/**
* Ôèëüòð äëÿ ïðîâåðêè àâòîðèçàöèè
*
* action: login(password, login)
* action: logout()
*/
// Â êëàññ àâòîðèçàöèè ïåðåäàâàòü îáüåêò äëÿ óïðàâëåíèÿ ïîëüçîâàòåëåì
// Âûíåñòè â îòäåëüíûé ôàéë
class LoginFilter extends Filter
{
const SESSION_BROWSER_SIGN_SECRET = '@w3dsju45Msk#';
const SESSION_BROWSER_SIGN_KEYNAME = 'session.app.browser.sign';
public $mode = 'ajax';
/**
* Ïðîâåðêà àâòîðèçàöèè
* @return Boolean Àâòîðèçîâàíè ïîëüçîâàòåëü èëè íåò
*/
public function isLoggin(Collection $request)
{
// Àâòîðèçàöèÿ
session_start();
$db = $this->getConnection();
UserAccess::setUp($db); // Ñîåäèíåíèå
switch ($request->getAction()) {
// Àâòîðèçàöèÿ ïî ïîñòîÿííîìó ïàðîëþ
case 'login':
$login = $request->get('login');
$password = $request->get('password');
$result = UserAccess::getUserByLogin($login); // Ïîèñê ïî ëîãèíó
if ($result) {
if (md5($password) == $result->getString('password')) { // password
$this->enter($db, $result);
return true;
}
}
$request->set('error', true);
break;
case 'logout': // Âûõîä
session_destroy();
break;
// Âõîä ïî âðåìåííîìó ïàðîëþ
case 'enter':
$login = $request->get('login');
$password = $request->get('sid');
$result = UserAccess::getUserByLogin($login); // Ïîèñê ïî ëîãèíó
if ($result) {
$temp = md5($result->getString('password') . $result->getString('login') . $result->getString('sid'));
if ($password == $temp) {
$this->enter($db, $result);
return true;
}
}
break;
default:
$hash = $this->getBrowserSign();
// Åñëè $hash íå ñîâïàäàåò $_SESSION['hash'] òî óäàëÿåì ñåññèþ
if (isset($_SESSION ['access']) && isset($_SESSION[self::SESSION_BROWSER_SIGN_SECRET])) {
if ($hash == $_SESSION[self::SESSION_BROWSER_SIGN_SECRET]) {
UserAccess::getUserById($_SESSION ['access']); // Ïîèñê ïî èäåíòèôèêàòîðó
return true;
} else {
session_destroy();
}
}
}
return false;
}
private function getBrowserSign()
{
$rawSign = self::SESSION_BROWSER_SIGN_SECRET;
$signParts = array('HTTP_USER_AGENT', 'HTTP_ACCEPT_ENCODING');
$rawSign = '';
foreach ($signParts as $signPart) {
$rawSign .= '::' . (isset($_SERVER[$signPart]) ? $_SERVER[$signPart] : 'none');
}
return md5($rawSign);
}
private function enter($db, $result)
{
$db->executeQuery("UPDATE users SET sid = '' WHERE id_user = " . $result->getInt('id_user'));
$_SESSION ["group"] = $result->getInt('access');
$_SESSION ["access"] = $result->getInt('id_user'); // id_user
$_SESSION [self::SESSION_BROWSER_SIGN_SECRET] = $this->getBrowserSign();
$_SESSION ["time"] = time();
}
public function execute(Collection $request)
{
if (!$this->isLoggin($request)) {
// Ïàðàìåòðû ïðè íåïðàâèëüíîé àâòîðèçàöèè
// Äåéñòâèÿ ïî óìîë÷àíèþ !! Âîçìîæíî ïåðåõîä íà ôîðìó ðåãèñòðàöèè
$request->set('module', 'login');
$request->set('mode', $this->mode);
}
return $this->processor->execute($request);
}
}

335
core/form.php Normal file
View file

@ -0,0 +1,335 @@
<?php
/**
* Deprecated !!!!
*/
require_once 'core/adapter.php';
/**
* Íîâîå API äëÿ Ôîðì
* $form = new Form ();
* $form->render () -> html,
* $form->adjust ($scheme);
* $form->input ($name, $type, $label, );
* $form->set($name, $value);
* $form->get($name); -> value
* $form->parse ($request),
* $form->validate () -> boolean,
* $form->values () -> pair[key] = value
*/
/**
* Ýëåìåíò ôîðìû
* @package core
*/
class TField {
protected $_value; // Ôîðìàòèðîâàííîå çíà÷åíèå ïîëÿ
var $label; // Ìåòêà ïîëÿ
var $rule = array ();// Ïðàâèëà äëÿ ïðîâåðêè ïîëÿ
var $value; // Ôîðìàòèðîâàííîå Çíà÷åíèå ïîëÿ
// var $default; // Çíà÷åíèå ïî óìîë÷àíèþ
var $error = false; // â XRule Ïðàâèëà äëÿ ïðîâåðêè çíà÷åíèé
var $error_msg = "Ïîëå íå ìîæåò áûòü ïóñòûì";
var $type; // Êàæäîìó òèïó ýëåìåíòà ñîîòâåòñòâóåò ìàêðîñ TAL
public function __construct ($input) {
// $this->deafult = null;
$this->require = false;
// Èíèöèàëèçàöèÿ ñâîéñò îáüåòêà
foreach ($input as $key => $value) {
$this->$key = $value;
}
}
public function __toString () {
return $this->value;
}
public function isValid ($name) {
if ($this->require == true && empty($this->value)) {
$this->error = true;
return false;
}
$this->setValue ($this->value);
return true;
}
// Äîáàâèòü ìåòîäû getString, setString ??
function setValue ($value) {
$this->_value = $value;
$this->value = $value;
}
function getValue () {
return $this->_value;
}
}
/**
* Ïîëå ââîäà Input
* @package core
*/
class TInput extends TField {
public function __construct ($input) {
parent::__construct ($input);
$this->setValue ("");
}
}
// checkbox
class TCheckbox extends TField {
public $checked = false;
public function __construct ($input) {
parent::__construct ($input);
$this->setValue (1);
}
function setValue ($value) {
$this->_value = intval ($value);
$this->value = 1;
if ($this->_value == 1) $this->checked = true; else $this->checked = false;
}
}
/**
* Âûáîð èç îäíîãî ýëåìåíòà
*/
class TSelect1 extends TField {
var $options = array ();
public function __construct ($input) {
parent::__construct ($input);
$this->setValue (0);
}
function setValue ($value) {
$this->_value = $value;
$this->value = $value;
foreach ($this->options as $key => $o) {
$this->options[$key]['selected'] = ($this->options[$key]['value'] == $this->_value);
}
}
}
class TSelectGroup extends TField {
var $groups = array ();
public function __construct ($input) {
parent::__construct ($input);
$this->setValue (0);
}
function setValue ($value) {
$this->_value = $value;
$this->value = $value;
foreach ($this->groups as $gkey => $o) {
foreach ($this->groups[$gkey]['options'] as $key => $v) {
$this->groups[$gkey]['options'][$key]['selected'] = ($this->groups[$gkey]['options'][$key]['value'] == $this->_value);
}
}
}
}
/**
* Ïîëå ñ äàòîé
* @package core
*/
class TDate extends TField {
var $error_msg = "Íåâåðíûé ôîðìàò äàòû";
var $separator = ".";
public function __construct ($input) {
parent::__construct ($input);
$this->setValue (time ());
}
function isValid ($name) {
$value = $this->value;
if ($tmp = explode(".", $value, 3)) {
if ($tmp[1] && $tmp[0] && $tmp[2]) {
if (checkdate ($tmp[1], $tmp[0], $tmp[2])) {
$this->setValue (mktime (0, 0, 0, $tmp[1], $tmp[0], $tmp[2]));
return true;
}
}
}
$this->error = true;
return false;
}
function setValue ($value) {
$this->_value = $value;
$this->value = date ("d.m.Y", $value);
}
}
class TTime extends TField {
var $error_msg = "Íåâåðíûé ôîðìàò âðåìåíè";
public function __construct ($input) {
parent::__construct ($input);
$this->setValue (mktime(0, 0, 0, 11, 30, 1999));
}
function isValid ($name) {
$value = $this->value;
if ($tmp = explode(":", $value, 2)) {
if ($this->checktime ($tmp[0], $tmp[1])) {
$this->setValue (mktime ($tmp[0], $tmp[1], 0, 0, 0, 0));
return true;
}
}
$this->error = true;
return false;
}
function checktime($hour, $minute) {
if ($hour > -1 && $hour < 24 && $minute > -1 && $minute < 60) {
return true;
}
}
function setValue ($value) {
$this->_value = $value;
$this->value = date ("H:i", $value);
}
}
/* *
* Òåêñòîâîå ïîëå
* @package core
*/
class TTextArea extends TField {
public function __construct ($input) {
parent::__construct ($input);
$this->setValue ("");
}
}
/**
* Ïîëå äëÿ ââîäà ïàðîëÿ
* @package core
*/
class TSecret extends TField {
public function __construct ($input) {
parent::__construct ($input);
$this->setValue ("");
}
}
class TUpload extends TField {
public $types = array ();
public function __construct ($input) {
parent::__construct ($input);
$this->setValue ("");
}
public function setValue ($value) {
$this->_value = basename ($value);
$this->value = $value;
}
}
/**
* Ôîðìà äëÿ ââîäà
* @package core
*/
class TForm {
var $field = array ();
var $action = "";
var $method = 'post';
var $request;
var $replace;
public function __construct ($request) {
$this->uid = get_form_uid ();
$this->constructor = array (
'input' => 'TInput',
'checkbox' => 'TCheckbox',
'date' => 'TDate',
'time' => 'TTime',
'textarea' => 'TTextArea',
'select' => 'TSelect',
'select1' => 'TSelect1',
'selgroup' => 'TSelectGroup',
'secret' => 'TSecret',
'upload' => 'TUpload'
);
$this->request = $request;
}
function get ($name) {
return $this->field [$name]->getValue ();
}
function addFieldObject ($name, $el) {
$this->field [$name] = $el;
}
/**
* Ìåòîä äîëæåí ïðîâåðÿòü çíà÷åíèÿ ïîëåé ôîðìû ïîëñëå çàïîëíåíèÿ
* Ïðîâåðêà ïðàâèëüíîñòè çàïîëíåíèÿ ôîðìû è óñòàíîâêà çíà÷åíèé
*/
function isValid () {
$haveErrors = false;
foreach ($this->field as $name => $el) { // ññûëêà
if ($this->field [$name] instanceof TUpload) {
// print_r ($_POST);
$filename = $this->request->getRawData ('files', $name);
if ((bool) $filename['name']) {
$this->field [$name]->value = $filename['name'];
} else {
$this->field [$name]->value = $this->request->getRawData ($this->method, $name."_file");
}
} else {
$this->field [$name]->value = $this->request->getRawData ($this->method, $name);
}
if (!$this->field [$name]->isValid($name)) {
$haveErrors = true;
}
}
return !$haveErrors;
}
/**
* Äîáàâëÿåò îäíî ïîëå ââîäà íà ôîðìó
*/
public function addField ($init) {
assert ($init['type']);
assert ($init['name']);
$constructor = $this->constructor[$init['type']];
$el = new $constructor ($init);
$el->type = $init['type'];
$this->addFieldObject ($init['name'], $el);
return $el;
}
/**
* Äîáàâëÿåò ñïñîê ïîëåé äëÿ ôîðìû
* @param array $list
*/
public function addFieldList ($list) {
foreach ($list as $init) {
$this->addField ($init);
}
}
/**
* Çàïîëíÿåò ôîðìó äàííûìè èç êîëëåêöèè
* Äëÿ îáüåêòîâ è ìàññèâîâ ìîæíî èñïîëüçîâàòü Adapter pattern
* @param object $data
* @param array $schema Ñâÿçü ìåæäó ýëåìåíòàìè ôîðìû è ñâîéñòâàìè îáüåêòà
*/
public function fill ($data) {
foreach ($this->field as $name => $el) {
$this->field [$name]->setValue ($data->get ($name));
}
}
}
?>

180
core/form/form.php Normal file
View file

@ -0,0 +1,180 @@
<?php
/**
* Ýëåìåíò ôîðìû
* @package core
*/
class TField
{
public $name;
public $label; // Ìåòêà ïîëÿ
public $value; // Çíà÷åíèå ïîëÿ
public $type; // Êàæäîìó òèïó ýëåìåíòà ñîîòâåòñòâóåò ìàêðîñ TAL
public $error_msg = null;
public $error = false;
public $require = false;
public function __construct ($input)
{
$this->deafult = null;
if (isset($input['validate'])) {
$this->require = strpos($input['validate'], 'require') !== false;
}
// Èíèöèàëèçàöèÿ ñâîéñò îáüåòêà
foreach (array('label', 'name', 'type') as $name) {
$this->$name = $input[$name];
}
}
function setValue($value)
{
$this->value = $value;
}
}
/**
* Ïîëå ââîäà Input
* @package core
*/
class TInput extends TField {
}
/**
* Âûáîð èç îäíîãî ýëåìåíòà
*/
class TSelect1 extends TField
{
public $options = array ();
public function __construct ($input) {
parent::__construct($input);
$this->options = $input['options'];
}
function setValue($value)
{
// Óñòàíîâèòü selected ó options
$this->value = $value;
}
}
/**
* Ïîëå ñ äàòîé
* @package core
*/
class TDate extends TField
{
}
/* *
* Òåêñòîâîå ïîëå
* @package core
*/
class TTextArea extends TField
{
}
/**
* Ïîëå äëÿ ââîäà ïàðîëÿ
* @package core
*/
class TSecret extends TField
{
}
class TUpload extends TField
{
}
/**
* Ôîðìà äëÿ ââîäà
* @package core
*/
class TForm
{
public $field = array ();
public $action = "";
public $method = 'post';
protected $replace;
protected $before;
public function __construct ()
{
$this->constructor = array (
'input' => 'TInput',
'date' => 'TDate',
'textarea' => 'TTextArea',
'select' => 'TSelect',
'select1' => 'TSelect1',
'secret' => 'TSecret',
'upload' => 'TUpload'
);
}
/**
* Äîáàâëÿåò îäíî ïîëå ââîäà íà ôîðìó
*/
public function addField (array $init)
{
assert (isset($init['type']));
assert (isset($init['name']));
$constructor = $this->constructor[$init['type']];
$el = new $constructor ($init);
$el->type = $init['type'];
$this->field [$init['name']] = $el;
return $el;
}
/**
* Äîáàâëÿåò ñïñîê ïîëåé äëÿ ôîðìû
* @param array $list
*/
public function addFieldList (array $list)
{
foreach ($list as $init) {
$this->addField ($init);
}
}
/**
* Óñòàíàâëèâàåò îøèáêè ïîñëå ïðîâåðêè
*/
function setError (Validator $validator)
{
foreach ($validator->getErrorMsg() as $name => $error)
{
$this->field[$name]->error = true;
$this->field[$name]->error_msg = $error;
}
}
/**
* Óñòàíàâëèâàåò çíà÷åíèÿ èç ìàñèâà
*/
function setValues (Collection $request) {
foreach ($this->field as $key => $el) {
$value = $request->getRawData ($this->method, $key);
$this->field[$key]->setValue($value);
}
}
/**
* Çàïîëíÿåò ôîðìó äàííûìè èç îáüåêòà
* @param object $data
* @param array $schema Ñâÿçü ìåæäó ýëåìåíòàìè ôîðìû è ñâîéñòâàìè îáüåêòà
*/
public function fill ($data, array $schema)
{
foreach ($schema as $key => $value) {
$this->field [$value]->setValue($data->$value->getString ());
}
}
public function set($name, $value)
{
$this->field[$name]->setValue($value);
}
}
?>

43
core/form/viewstate.php Normal file
View file

@ -0,0 +1,43 @@
<?php
/**
* http://www.alternateinterior.com/2006/09/a-viewstate-for-php.html
* Óïðàâëåíèå ñîñòîÿíèåì ìåæäó ñòðàíèöàìè
*/
class ViewState // extends Collection
{
private $values = array();
function set($name, $value)
{
$this->values[$name] = $value;
}
function get()
{
$args = func_get_args();
$result = $this->values;
foreach ($args as $name) {
if (!isset($result[$name])) {
return null;
}
$result = $result[$name];
}
return $result;
}
function saveState()
{
return base64_encode(serialize($this->values));
}
function restoreState($value)
{
$this->values = unserialize(base64_decode($value));
}
function export()
{
return $this->values;
}
}

52
core/formats/dot.php Normal file
View file

@ -0,0 +1,52 @@
<?php
/**
* Ãåíåðàöèÿ ôàéëîâ Grpahviz dot
*/
class Dot
{
static function getHeader ()
{
$header =
"digraph G {\n"
. "\toverlap=false; splines=true;\n"
. "\tfontname = \"Verdana\"\n"
. "\tfontsize = 8\n"
. "\tnode [\n"
. "\t\tfontname = \"Verdana\"\n"
. "\t\tfontsize = 8\n"
. "\t\tshape = \"record\"\n"
. "\t]\n"
. "\tedge [\n"
. "\t\tfontname = \"Verdana\"\n"
. "\t\tfontsize = 8\n"
. "\t]\n";
return $header;
}
static function getFooter ()
{
$footer = "}\n";
return $footer;
}
function assocToDot (array $array, array $label)
{
$result = array (self::getHeader());
// Ìåòêè
foreach ($label as $value) {
$result [] = "\t\"{$value[0]}\" [ label = \"{$value[1]}\" ] \n";
}
// Àññîöèàöèè
foreach ($array as $key => $value) {
foreach ($value as $n) {
$result [] = "\t\"$key\" -> \"$n\"\n";
}
}
$result [] = self::getFooter();
return implode("", $result);
}
}
?>

86
core/formats/helix.php Normal file
View file

@ -0,0 +1,86 @@
<?php
define ('URL_CLIP', 0);
define ('URL_DIRECTORY', 1);
class HAuthorize {
public $password;
public $uuid;
public $uuid_writeable;
public function __construct () {
$this->uuid = "*";
$this->uuid_writeable = 1;
}
public function getString () {
return "{$this->password};{$this->uuid};{$this->uuid_writeable}";
}
public function setPassword ($username, $realm, $password) {
$this->password = md5 ("$username:$realm:$password");
}
}
class HPermission {
public $url;
public $url_type;
public $permission_type;
public $expires;
public $debitted_time;
public function __construct ($url, $url_type) {
$this->url = $url;
$this->url_type = $url_type;
$this->expires = "*"; // MM/DD/YYYY:HH:MM:SS
$this->permission_type = 0;
$this->debitted_time = 0;
}
public function getString () {
return "{$this->url};{$this->url_type};{$this->permission_type};{$this->expires};{$this->debitted_time}";
}
}
class HFile {
public $authorize;
public $permission = array ();
public function addAuthorize ($name, $realm, $password) {
$this->authorize = new HAuthorize ();
$this->authorize->setPassword ($name, $realm, $password);
}
public function addPermission ($url, $url_type) {
$this->permission[] = new HPermission ($url, $url_type);
}
public function write ($name) {
$file = fopen ($name, 'w');
fwrite ($file, $this->getString ());
fclose ($file);
}
public function writeFTP ($ftp, $path) {
$file = tmpfile ();
fwrite ($file, $this->getString ());
fseek ($file, 0);
ftp_fput ($ftp, $path, $file, FTP_BINARY);
fclose ($file);
}
public function getString () {
$result = array ();
$result[] = $this->authorize->getString ();
$result[] = "\n";
foreach ($this->permission as $p) {
$result[] = $p->getString ();
$result[] = "\n";
}
return implode ("", $result);
}
}
?>

346
core/functions.php Normal file
View file

@ -0,0 +1,346 @@
<?php
/**
* Ôóíêöèîíàëüíîå ïðîãðàììèðîâàíèå â PHP
* package functional
*/
/**
* Ýìóëÿöèÿ êàððèðîâàíîé ôóíêöèè
*/
class __right {
protected $params;
protected $fn;
public function __construct($params) {
$this->fn = array_shift($params);
$this->params = $params;
}
function apply() {
$params = func_get_args();
array_splice($params, count($params), 0, $this->params);
return call_user_func_array($this->fn, $params);
}
}
class __left {
protected $params;
protected $fn;
public function __construct($params) {
$this->fn = array_shift($params);
$this->params = $params;
}
function apply() {
$params = func_get_args();
array_splice ($params, 0, 0, $this->params);
return call_user_func_array ($this->fn, $params);
}
}
define('__', '_ARGUMENT_PLACE_');
class __partial {
protected $params;
protected $fn;
public function __construct($params) {
$this->fn = array_shift($params);
$this->params = $params;
}
function apply() {
$params = func_get_args();
$result = array();
for($i = 0, $j = 0; $i < count($this->params); $i++) {
if ($this->params[$i] == __) {
$result [] = $params[$j];
$j++;
} else {
$result [] = $this->params[$i];
}
}
return call_user_func_array ($this->fn, $result);
}
}
function partial() {
$closure = new __partial(func_get_args());
return array($closure, 'apply');
}
/**
* Êîìïîçèöèÿ ôóíêöèé
*/
class __compose {
protected $fns;
function __construct($list) {
$this->fns = array_reverse($list);
}
function apply () {
$params = func_get_args ();
$result = call_user_func_array($this->fns[0], $params);
for ($i = 1; $i < count($this->fns); $i++) {
$result = call_user_func($this->fns[$i], $result);
}
return $result;
}
}
/**
* Êîìïîçèöèÿ ôóíêöèé
* @param mixed $a
* @param mixed $b
*
* @return array[int]mixed
*/
function compose() {
$closure = new __compose(func_get_args());
return array($closure, 'apply');
}
/**
* Êàðèðîâàíèå ñïðàâà
*
* @return array[int]mixed
*/
function rcurry() {
$closure = new __right(func_get_args ());
return array($closure, 'apply');
}
/**
* Êàðèðîâàíèå ñëåâà
*
* @return array[int]mixed
*/
function lcurry() {
$closure = new __left(func_get_args ());
return array($closure, 'apply');
}
/**
* Ðàçäåëåíèå ìàññèâà íà äâà ïî óñëîâèþ
* @param mixed $pred Óñëîâèå ïî êîòîðîìó ðàçäåëÿåòñÿ ìàññèâ
* @param array $lst
*
* @return array[int]mixed
*/
function partition($pred, $lst) {
$left = array ();
$right = array ();
foreach ($lst as $n) {
if (call_user_func($pred, $n) !== false) {
$left [] = $n;
} else {
$right [] = $n;
}
}
return array ($left, $right);
}
/**
* @param array $value
* @param string $name
*
* @return mixed
*/
function __key($value, $name) {
return $value[$name];
}
function identity($value) {
return $value;
}
/**
* @param array $a
* @param array $b
* @param $key
*
* @return int
*/
function __cmp($a, $b, $key) {
if ($a[$key] == $b[$key]) {
return 0;
}
return ($a[$key] > $b[$key]) ? -1 : 1;
}
function __cmp_less($a, $b, $key) {
if ($a[$key] == $b[$key]) {
return 0;
}
return ($a[$key] < $b[$key]) ? -1 : 1;
}
// Ñðàâíåíèå ïî êëþ÷ó ìàññèâå
function __index($n, $key, $row) {
return ($row[$key] == $n);
}
function __div($x, $y) {
return $x / $y;
}
function __self($name, $o) {
return call_user_func(array($o, $name));
}
function concat(/* $args ...*/) {
$args = func_get_args();
return implode($args);
}
function __empty($x) {
return empty($x);
}
// Îòðèöàíèå
function __not($x) {
return !$x;
}
// Íå ðàâíî
function __neq($x, $y) {
return $x != $y;
}
// Ðàâíî
function __eq($x, $y) {
return $x == $y;
}
/**
* Èçâëåêàåò èç ìíîãîìåðîãî ìàññèâà çíà÷åíèÿ ñ îïðåäåëåííûì êëþ÷îì
* @example key_values('a', array(1 => array('a' => 1, 'b' => 2))) => array(1)
*
* @return mixed
*/
function key_values($key, /*array|ArrayIterator*/ $array) {
$result = array();
foreach($array as $item) {
$result[] = $item[$key];
}
return $result;
}
function assoc_key_values($key, $value, $array) {
$result = array();
foreach ($array as $item) {
$result[$item[$key]] = $item[$value];
}
return $result;
}
function assoc_key($key, $array) {
$result = array();
foreach ($array as $item) {
$result[$item[$key]] = $item;
}
return $result;
}
function _get($key, $value, $array) {
foreach ($array as $item) {
if ($item[$key] == $value) return $item;
}
return null;
}
function _get_key($key, $value, $array) {
foreach ($array as $name => $item) {
if ($item[$key] == $value) return $name;
}
return null;
}
/**
* Ëîãè÷åñêà îïåðàöèÿ && êî âñåì ýëåìåíòàì ìàññèâà
* @return bool
*/
function every(array $array, $callback) {
foreach ($array as $key => $value) {
if (call_user_func($callback, $value) === false) {
return false;
}
}
return true;
}
/**
* Ëîãè÷åñêà îïåðàöèÿ || êî âñåì ýëåìåíòàì ìàññèâà
* @param array $array
* @param mixed $callback
*
* @return mixed
*/
function some(array $array, $callback) {
assert(is_callable($callback));
foreach ($array as $key => $value) {
if (call_user_func($callback, $value) === true) {
return $key;
}
}
return false;
}
function span($length, array $array) {
assert(is_int($length));
$result = array();
for($i = 0; $i < count($array); $i += $length) {
$result [] = array_slice($array, $i, $length);
}
return $result;
}
function array_ref($data, $n) {
return $data[$n];
}
function call() {
$args = func_get_args();
$name = array_shift($args);
return call_user_func_array($name, $args);
}
/**
* Ïîèñê ýëåìåíòà â ìàññèâå
* @param function $cb ñðàâíåíèå ñ ýëåìåíòîì ìàññèâà
* @param array $hs ìàññèâ â êîòîðîì èùåòñÿ çíà÷åíèå
*
* @return int|string êëþ÷ íàéäåíîãî ýëåìåíòà â ìàññèâå
*/
function array_usearch($cb, array $hs, $strict = false) {
foreach($hs as $key => $value) if (call_user_func_array($cb, array($value, $key, $strict))) return $key;
}
if (!function_exists('hash_key')) {
/**
* Ïðåîáðàçóåò êëþ÷è ýëåìåíòîâ äëÿ ìíîãîìåðíîãî ìàññèâà
* @return mixed
*/
function hash_key ($key_name,/*. array .*/ $array) {
$result = array();
foreach($array as $value) {
$result[$value[$key_name]] = $value;
}
return $result;
};
}
function array_merge1($x, $y) {
$result = $x;
foreach ($y as $k => $v) {
$result [$k] = $v;
}
return $result;
}

13
core/geometry/point.php Normal file
View file

@ -0,0 +1,13 @@
<?php
class Point
{
public $left, $top;
function __construct ($left = 0, $top = 0)
{
$this->left = $left;
$this->top = $top;
}
}
?>

Some files were not shown because too many files have changed in this diff Show more