Footnotes: Fixed PHPdoc
/trunk/footnotes.class.php |
---|
0,0 → 1,294 |
<?php |
/** |
* A footnote list contains {@link Footnote Footnotes} |
* |
* @author Thomas 'PointedEars' Lahn <php@PointedEars.de> |
*/ |
class FootnoteList |
{ |
/** |
* The footnotes of this list |
* |
* @var Array |
*/ |
protected $_footnotes; |
/** |
* Last used number sign for a footnote |
* |
* @var int |
*/ |
protected $_lastNumberSign; |
protected $_defaultPrefix; |
protected $_defaultSuffix; |
protected $_makeTooltip; |
/* (non-PHPdoc) @see Footnote::_html4Compat */ |
protected $_html4Compat; |
public function __construct($defaultPrefix = '', $defaultSuffix = '', $makeTooltip = false, $html4Compatible = false) |
{ |
$this->clear(); |
$this->_defaultPrefix = $defaultPrefix; |
$this->_defaultSuffix = $defaultSuffix; |
$this->_makeTooltip = $makeTooltip; |
$this->_html4Compat = $html4Compatible; |
} |
public function __get($name) |
{ |
if (property_exists($this, "_$name")) |
{ |
return $this->{"_$name"}; |
} |
throw new InvalidArgumentException( |
'No such property ' . get_class($this) . "::\$$name"); |
} |
/** |
* Clears the footnote list |
*/ |
public function clear() |
{ |
$this->_footnotes = array(); |
$this->_lastNumberSign = 0; |
} |
/** |
* Adds a footnote to the list (unless already specified) |
* |
* @param string $name |
* Name of the footnote |
* @param string $sign |
* Sign of the footnote. If empty, the next available number is used. |
* @param string $text |
* Text for the footnote |
* @param string $tooltip |
* Tooltip for the footnote |
* @return string |
* The code for printing the footnote reference. |
*/ |
public function add($name, $sign = '', $text = '', $tooltip = null) |
{ |
$footnotes =& $this->_footnotes; |
if (!isset($footnotes[$name])) |
{ |
if (!$sign) |
{ |
$sign = ++$this->_lastNumberSign; |
} |
else if (is_int($sign)) |
{ |
$this->_lastNumberSign = $sign; |
} |
$footnotes[$name] = new Footnote($name, $sign, $text, |
$this->_defaultSuffix, $this->_defaultPrefix, |
$this->_makeTooltip ? ($tooltip !== null ? $tooltip : $text) : '', |
$this->_html4Compat); |
} |
return $footnotes[$name]->getRef(); |
} |
/** |
* Prints the list of footnotes |
*/ |
public function printMe() |
{ |
$footnotes =& $this->_footnotes; |
uasort($footnotes, function ($a, $b) { |
/* Sort numbered footnotes first */ |
if (is_int($a->sign) && !is_int($b->sign)) |
{ |
return -1; |
} |
if (is_int($b->sign) && !is_int($a->sign)) |
{ |
return 1; |
} |
/* Sort footnotes either numerically or alphabetically */ |
/* TODO: Sort "1b" before "12a" */ |
if ($a->sign < $b->sign) |
{ |
return -1; |
} |
if ($a->sign > $b->sign) |
{ |
return 1; |
} |
return 0; |
}); |
?><table class="footnotes"> |
<?php |
foreach ($footnotes as $name => &$footnote) |
{ |
$footnote->printMe(); |
} |
?></table><?php |
} |
/** |
* Prints the list of footnotes and clears the list in memory |
*/ |
public function flush() |
{ |
$this->printMe(); |
$this->clear(); |
} |
} |
/** |
* A footnote to be used in a {@link FootnoteList "footnote list"} |
* |
* @author Thomas 'PointedEars' Lahn <php@PointedEars.de> |
*/ |
class Footnote |
{ |
/** |
* The name of this footnote |
* |
* @var string |
*/ |
protected $_name = ''; |
/** |
* The sign used for referring to this footnote |
* |
* @var string |
*/ |
protected $_sign = ''; |
/** |
* The text for this footnote |
* |
* @var string |
*/ |
protected $_text = ''; |
protected $_prefix = ''; |
protected $_suffix = ''; |
protected $_tooltip = ''; |
/** |
* HTML 4 compatibility: If <code>true</code>, generates |
* <code>name</code> attributes. Default: <code>false</code>. |
* |
* @var boolean |
*/ |
protected $_html4Compat = false; |
/** |
* The number of times this footnote has been referred |
* |
* @var int |
*/ |
protected $_references = 0; |
/** |
* Creates a footnote |
* |
* @param string $name |
* The name of this footnote |
* @param string $sign |
* The sign that should be used for referring to this footnote |
* @param string $text |
* The text for this footnote |
* @param string $suffix |
* The suffix for this footnote |
* @param string $prefix |
* The prefix for this footnote |
*/ |
public function __construct($name, $sign, $text, $suffix = '', $prefix = '', |
$tooltip = '', $html4Compatible = false) |
{ |
$this->_name = $name; |
$this->_sign = (is_numeric($sign) ? (int) $sign : $sign); |
$this->_text = $text; |
$this->_suffix = $suffix; |
$this->_prefix = $prefix; |
$this->_tooltip = $tooltip; |
$this->_html4Compat = $html4Compatible; |
} |
/** |
* Universal getter |
* |
* @param string $name |
* Name of the property to be read-accessed. Currently only 'sign' |
* is supported. |
* @throws InvalidArgumentException if a non-existing property is accessed |
* @return mixed |
* Property value |
*/ |
public function __get($name) |
{ |
if (property_exists($this, "_$name")) |
{ |
return $this->{"_$name"}; |
} |
throw new InvalidArgumentException( |
'No such property ' . get_class($this) . "::\$$name"); |
} |
/** |
* Returns the reference for this footnote |
* |
* @return string |
*/ |
public function getRef() |
{ |
$s = $this->_name; |
$ret = "<sup><a href='#footnote-{$s}'" |
. ($this->_references === 0 |
? (($this->_html4Compat ? " name='fn-{$s}-ref'" : "") . " id='fn-{$s}-ref'") |
: '') |
. ' class="footnote"' |
. ($this->_tooltip |
? ' title="' |
. preg_replace('/"/', '"', |
trim(reduceWhitespace(strip_tags($this->_tooltip)))) |
. '"' |
: '') |
. ">{$this->_prefix}{$this->_sign}{$this->_suffix}" |
. '</a></sup>'; |
++$this->_references; |
return $ret; |
} |
/** |
* Prints this footnote in a footnote list |
*/ |
public function printMe() |
{ |
$s = $this->_name; |
echo " <tr> |
<th><sup><a" . ($this->_html4Compat ? " name='footnote-{$s}'" : "") |
. " id='footnote-{$s}' class='footnote' |
>{$this->_sign}</a></sup><a |
href='#fn-{$s}-ref' class='backref'>↑</a></th> |
<td>{$this->_text}</td> |
</tr> |
"; |
} |
} |
?> |
/trunk/Db/Database.php |
---|
0,0 → 1,1060 |
<?php |
namespace PointedEars\PHPX\Db; |
require_once __DIR__ . '/../global.inc'; |
/** |
* Generic database model class using PDO (PHP Data Objects) |
* |
* @property-read PDO $connection |
* Database connection. Established on read access to this |
* property if not yet established. |
* @property-read array $lastError |
* Last error information of the database operation. |
* See {@link PDOStatement::errorInfo()}. |
* @property-read string $lastInsertId |
* ID of the last inserted row, or the last value from a sequence object, |
* depending on the underlying driver. May not be supported by all databases. |
* @property-read array $lastResult |
* Last result of the database operation |
* @property-read boolean $lastSuccess |
* Last success value of the database operation |
* @author Thomas Lahn |
*/ |
class Database extends \PointedEars\PHPX\AbstractModel |
{ |
/* Access properties */ |
/** |
* DSN of the database |
* @var string |
*/ |
protected $_dsn = ''; |
/** |
* Username to access the database |
* @var string |
*/ |
protected $_username; |
/** |
* Password to access the database |
* @var string |
*/ |
protected $_password; |
/** |
* PDO driver-specific options |
* @var array |
*/ |
protected $_options = array(); |
/** |
* Database-specific string to use for quoting a name or value |
* left-hand side (for security reasons and to prevent a name |
* from being parsed as a keyword). |
* @var string |
*/ |
protected $_leftQuote = ''; |
/** |
* Database-specific string to use for quoting a name or value |
* left-hand side (for security reasons and to prevent a name |
* from being parsed as a keyword). |
* @var string |
*/ |
protected $_rightQuote = ''; |
/* Status properties */ |
/** |
* Database connection |
* @var PDO |
*/ |
protected $_connection; |
/** |
* Last success value of the database operation |
* @var boolean |
*/ |
protected $_lastSuccess; |
/** |
* Last error information of the database operation |
* @var array |
*/ |
protected $_lastError; |
/** |
* Last result of the database operation |
* @var array |
*/ |
protected $_lastResult; |
/** |
* ID of the last inserted row, or the last value from a sequence object, |
* depending on the underlying driver. May not be supported by all databases. |
* @var string |
*/ |
protected $_lastInsertId = ''; |
/** |
* Creates a new <code>Database</code> instance. |
* |
* Each of the parameters is optional and can also be given |
* by a protected property where the parameter name is preceded |
* by <code>_</code>. Parameter values overwrite the default |
* property values. It is recommended to use default property |
* values of inheriting classes except for small applications |
* and testing purposes. |
* |
* @param string $dsn |
* @param string $username |
* @param string $password |
* @param array $options |
* @see PDO::__construct() |
*/ |
public function __construct ($dsn = '', $username = null, |
$password = null, array $options = array()) |
{ |
if ($dsn !== '') |
{ |
$this->_dsn = $dsn; |
} |
if ($username !== null) |
{ |
$this->_username = $username; |
} |
if ($password !== null) |
{ |
$this->_password = $password; |
} |
if ($options) |
{ |
$this->_options = $options; |
} |
} |
/** |
* Reads the connection configuration for this database |
* from the configuration file, .config |
* |
* There must be an INI section named "database:" followed |
* by the value of the <code>$_dbname</code> property |
* containing keys and values for the properties of the |
* <code>Database</code> instance. Except for the key |
* <code>dbname</code>, which allows for aliases, all |
* keys are ignored if the corresponding properties |
* were set. That is, definitions in the class file |
* override those in the configuration file. |
* |
* @return array|boolean |
* The configuration array if the configuration |
* file could be read, <code>false</code> otherwise. |
*/ |
public function readConfig () |
{ |
/* FIXME: Configuration file path should not be hardcoded */ |
$config = parse_ini_file('.config', true); |
if ($config !== false) |
{ |
$section = 'database:' . $this->_dbname; |
if (isset($config[$section])) |
{ |
$dbconfig = $config[$section]; |
$options = array( |
'host', 'port', 'dbname', 'username', 'password', 'charset' |
); |
foreach ($options as $key) |
{ |
$property = "_$key"; |
if (isset($dbconfig[$key]) |
&& ($key == 'dbname' |
|| (property_exists($this, $property) |
&& $this->$property === null))) |
{ |
$this->$property = $dbconfig[$key]; |
} |
} |
return $config[$section]; |
} |
} |
return $config; |
} |
/** |
* @return PDO |
*/ |
public function getConnection () |
{ |
if ($this->_connection === null) |
{ |
$this->_connection = |
new \PDO($this->_dsn, $this->_username, $this->_password, $this->_options); |
} |
return $this->_connection; |
} |
/** |
* Creates a database according to the specified parameters |
* |
* Should be overwritten and called by inheriting classes. |
* |
* @param string $dsn |
* Connection DSN (required; must not include the database |
* name). |
* @param string $username = null |
* Connection username. The default is specified by the |
* <code>$_username</code> property. Note that creating |
* the database usually requires a user with more privileges |
* than the one accessing the database or its tables. |
* @param string $password = null |
* Connection password. The default is specified by the |
* <code>$_password</code> property. |
* @param array? $options = null |
* Connection options. The default is specified by the |
* <code>$_options</code> property. |
* @param string $spec = null |
* Additional database specifications, like character encoding |
* and collation. |
* @param boolean $force = false |
* If a true-value, the database will be attempted to be |
* created even if there is a database of the name specified |
* by the <code>$_dbname</code> property. |
* @return int |
* The number of rows affected by the CREATE DATABASE statement. |
* @see PDO::__construct() |
* @see PDO::exec() |
*/ |
public function create ($dsn, $username = null, $password = null, |
array $options = null, $dbspec = null, $force = false) |
{ |
$connection = new \PDO($dsn, |
$username !== null ? $username : $this->_username, |
$password !== null ? $password : $this->_password, |
$options !== null ? $options : $this->_options); |
$query = 'CREATE DATABASE' |
. (!$force ? ' IF NOT EXISTS' : '') |
. ' ' . $this->escapeName($this->_dbname) |
. ($dbspec ? ' ' . $dbspec : ''); |
return $connection->exec($query); |
} |
/** |
* Maps column meta-information to a column definition. |
* |
* Should be overwritten and called by inheriting classes. |
* |
* @todo |
* @param array $value |
* @param string $column_name |
* @return string |
*/ |
protected function _columnDef (array $data, $column_name) |
{ |
$def = (isset($data['unsigned']) && $data['unsigned'] ? 'UNSIGNED ' : '') |
. $data['type'] |
. (isset($data['not_null']) && $data['not_null'] ? ' NOT NULL' : ' NULL') |
. (isset($data['default']) && $data['default'] ? " DEFAULT {$data['default']}" : '') |
. (isset($data['auto_inc']) && $data['auto_inc'] ? ' AUTO_INCREMENT' : '') |
. (isset($data['unique']) && $data['unique'] ? ' UNIQUE KEY' : '') |
. (isset($data['primary']) && $data['primary'] ? ' PRIMARY KEY' : '') |
. (isset($data['comment']) && $data['comment'] ? " COMMENT '{$data['comment']}'" : ''); |
return $this->escapeName($column_name) . ' ' . $def; |
} |
/** |
* Creates a database table according to the specified parameters |
* |
* @todo |
* @param string $name |
* @param array $columns |
* @param array $options = null |
* @return bool |
* @see PDOStatement::execute() |
*/ |
public function createTable ($name, array $columns, array $options = null) |
{ |
$class = \get_class($this); |
$query = 'CREATE TABLE ' |
. $this->escapeName($name) |
. '(' |
. array_map(array($this, '_columnDef'), $columns, array_keys($columns)) . ')'; |
$stmt = $this->prepare($query); |
/* DEBUG */ |
if (defined('DEBUG') && DEBUG > 1) |
{ |
debug(array( |
'query' => $query, |
)); |
} |
$success =& $this->_lastSuccess; |
$success = $stmt->execute(); |
$errorInfo =& $this->_lastError; |
$errorInfo = $stmt->errorInfo(); |
$this->_resetLastInsertId(); |
$result =& $this->_lastResult; |
$result = $stmt->fetchAll(); |
if (defined('DEBUG') && DEBUG > 1) |
{ |
debug(array( |
'_lastSuccess' => $success, |
'_lastError' => $errorInfo, |
'_lastResult' => $result |
)); |
} |
return $success; |
} |
/** |
* Initiates a transaction |
* |
* @return bool |
* @see PDO::beginTransaction() |
*/ |
public function beginTransaction() |
{ |
return $this->connection->beginTransaction(); |
} |
/** |
* Rolls back a transaction |
* |
* @return bool |
* @see PDO::rollBack() |
*/ |
public function rollBack() |
{ |
return $this->connection->rollBack(); |
} |
/** |
* Commits a transaction |
* |
* @return bool |
* @see PDO::commit() |
*/ |
public function commit() |
{ |
return $this->connection->commit(); |
} |
/** |
* Prepares a statement for execution with the database |
* @param string $query |
*/ |
public function prepare($query, array $driver_options = array()) |
{ |
return $this->connection->prepare($query, $driver_options); |
} |
/** |
* Returns the ID of the last inserted row, or the last value from |
* a sequence object, depending on the underlying driver. |
* |
* @return int |
*/ |
public function getLastInsertId() |
{ |
return $this->_lastInsertId; |
} |
/** |
* Returns the date of last modification of this database or |
* one of its tables. |
* |
* To be overridden by inheriting classes. |
* |
* @param string $table (optional) |
* Table name. If not provided, all tables of this database |
* are considered. |
* @return int|null |
* Timestamp of last modification, or <code>null</code> if |
* unavailable. |
*/ |
public function getLastModified ($table = null) |
{ |
return null; |
} |
/** |
* Escapes a database name so that it can be used in a query. |
* |
* @param string $name |
* The name to be escaped |
* @return string |
* The escaped name |
*/ |
public function escapeName($name) |
{ |
return $this->_leftQuote . $name . $this->_rightQuote; |
} |
/** |
* Determines if an array is associative (has not all integer keys). |
* |
* @author |
* Algorithm courtesy of squirrel, <http://stackoverflow.com/a/5969617/855543>. |
* @param array $a |
* @return boolean |
* <code>true</code> if <var>$a</var> is associative, |
* <code>false</code> otherwise |
*/ |
protected function _isAssociativeArray(array $a) |
{ |
for (reset($a); is_int(key($a)); next($a)); |
return !is_null(key($a)); |
} |
/** |
* Escapes an associative array so that its string representation can be used |
* as list with table or column aliases in a query. |
* |
* This method does not actually escape anything; it only inserts the |
* 'AS' keyword. It should be overridden by inheriting methods. |
* |
* NOTE: This method intentionally does not check whether the array actually |
* is associative. |
* |
* @param array &$array |
* The array to be escaped |
* @return array |
* The escaped array |
*/ |
protected function _escapeAliasArray(array &$array) |
{ |
foreach ($array as $column => &$value) |
{ |
$quotedColumn = $column; |
if (strpos($column, $this->_leftQuote) === false |
&& strpos($column, $this->_rightQuote) === false) |
{ |
$quotedColumn = $this->_leftQuote . $column . $this->_rightQuote; |
} |
$value = $value . ' AS ' . $quotedColumn; |
} |
return $array; |
} |
/** |
* @param array $a |
* @param string $prefix |
*/ |
private static function _expand(array $a, $prefix) |
{ |
$a2 = array(); |
foreach ($a as $key => $value) |
{ |
$a2[] = ':' . $prefix . ($key + 1); |
} |
return $a2; |
} |
/** |
* Escapes an associative array so that its string representation can be used |
* as value list in a query. |
* |
* This method should be overridden by inheriting classes to escape |
* column names as fitting for the database schema they support. It is |
* strongly recommended that the overriding methods call this method with |
* an appropriate <var>$escape</var> parameter, pass all other parameters |
* on unchanged, and return its return value. |
* |
* NOTE: Intentionally does not check whether the array actually is associative! |
* |
* @param array &$array |
* The array to be escaped |
* @param string $suffix |
* The string to be appended to the column name for the value placeholder. |
* The default is the empty string. |
* @param array $escape |
* The strings to use left-hand side (index 0) and right-hand side (index 1) |
* of the column name. The default is the empty string, respectively. |
* @return array |
* The escaped array |
*/ |
protected function _escapeValueArray(array &$array, $suffix = '') |
{ |
$result = array(); |
foreach ($array as $column => $value) |
{ |
$op = '='; |
$placeholder = ":{$column}"; |
if (is_array($value) && $this->_isAssociativeArray($value)) |
{ |
reset($value); |
$op = ' ' . key($value) . ' '; |
$value = $value[key($value)]; |
} |
if (is_array($value)) |
{ |
$placeholder = '(' . implode(', ', self::_expand($value, $column)) . ')'; |
} |
$result[] = $this->_leftQuote . $column . $this->_rightQuote . "{$op}{$placeholder}{$suffix}"; |
} |
return $result; |
} |
/** |
* Constructs the WHERE part of a query |
* |
* @param string|array $where |
* Condition |
* @param string $suffix |
* The string to be appended to the column name for the value placeholder, |
* passed on to {@link Database::_escapeValueArray()}. The default is |
* the empty string. |
* @return string |
* @see Database::_escapeValueArray() |
*/ |
protected function _where($where, $suffix = '') |
{ |
if (!is_null($where)) |
{ |
if (is_array($where)) |
{ |
if (count($where) < 1) |
{ |
return ''; |
} |
if ($this->_isAssociativeArray($where)) |
{ |
$where = $this->_escapeValueArray($where, $suffix); |
} |
$where = '(' . implode(') AND (', $where) . ')'; |
} |
return ' WHERE ' . $where; |
} |
return ''; |
} |
/** |
* Selects data from one or more tables; the resulting records are stored |
* in the <code>result</code> property and returned as an associative array, |
* where the keys are the column (alias) names. |
* |
* @param string|array[string] $tables Table(s) to select from |
* @param string|array[string] $columns Column(s) to select from (optional) |
* @param string|array $where Condition (optional) |
* @param string $order Sort order (optional) |
* If provided, MUST start with ORDER BY or GROUP BY |
* @param string $limit Limit (optional) |
* @param int $fetch_style |
* The mode that should be used for {@link PDOStatement::fetchAll()}. |
* The default is {@link PDO::FETCH_ASSOC}. |
* @param bool $fetchAll |
* If <code>true</code> (default), fetches all rows at once. |
* You can set this to <code>false</code> in case of memory problems, |
* in which case this function will return the prepared |
* {@link PDOStatement} instead of the result array. You can then use |
* {@link PDOStatement::fetch()} to get the returned rows iteratively. |
* <var>$fetch_style</var> will be ignored then, so that you can safely |
* pass <code>null</code> for it, for example. |
* @return array|PDOStatement |
* @see Database::prepare() |
* @see PDOStatement::fetchAll() |
*/ |
public function select($tables, $columns = null, $where = null, |
$order = null, $limit = null, $fetch_style = \PDO::FETCH_ASSOC, |
$fetchAll = true) |
{ |
if (is_null($columns)) |
{ |
$columns = array('*'); |
} |
if (is_array($columns)) |
{ |
if ($this->_isAssociativeArray($columns)) |
{ |
$columns = $this->_escapeAliasArray($columns); |
} |
$columns = implode(', ', $columns); |
} |
if (is_array($tables)) |
{ |
if ($this->_isAssociativeArray($columns)) |
{ |
$columns = $this->_escapeAliasArray($columns); |
} |
$tables = implode(', ', $tables); |
} |
$query = "SELECT {$columns} FROM {$tables}" . $this->_where($where); |
if (!is_null($order)) |
{ |
if (is_array($order)) |
{ |
$order = 'ORDER BY ' . implode(', ', $order); |
} |
$query .= " $order"; |
} |
if (!is_null($limit)) |
{ |
$query .= " LIMIT $limit"; |
} |
$stmt = ($fetchAll |
? $this->prepare($query) |
: $this->prepare($query, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL))); |
$params = array(); |
if (is_array($where) && $this->_isAssociativeArray($where)) |
{ |
/* FIXME: Export and reuse this */ |
foreach ($where as $column => $condition) |
{ |
/* TODO: Also handle function calls as keys */ |
if (is_array($condition) && $this->_isAssociativeArray($condition)) |
{ |
reset($condition); |
$condition = $condition[key($condition)]; |
if (is_array($condition)) |
{ |
foreach (self::_expand($condition, $column) as $param_index => $param_name) |
{ |
$params[$param_name] = $condition[$param_index]; |
} |
} |
} |
else |
{ |
$params[":{$column}"] = $condition; |
} |
} |
} |
/* DEBUG */ |
if (defined('DEBUG') && DEBUG > 1) |
{ |
debug(array( |
'query' => $query, |
'params' => $params |
)); |
} |
$success =& $this->_lastSuccess; |
$success = $stmt->execute($params); |
$errorInfo =& $this->_lastError; |
$errorInfo = $stmt->errorInfo(); |
$result =& $this->_lastResult; |
$result = ($fetchAll |
? $stmt->fetchAll($fetch_style) |
: $stmt); |
if (defined('DEBUG') && DEBUG > 1) |
{ |
debug(array( |
'_lastSuccess' => $success, |
'_lastError' => $errorInfo, |
'_lastResult' => $result |
)); |
} |
return $result; |
} |
/** |
* Sets and returns the ID of the last inserted row, or the last value from |
* a sequence object, depending on the underlying driver. |
* |
* @param string $name |
* Name of the sequence object from which the ID should be returned. |
* @return string |
*/ |
protected function _setLastInsertId($name = null) |
{ |
return ($this->_lastInsertId = $this->connection->lastInsertId($name)); |
} |
/** |
* Resets the the ID of the last inserted row, or the last value from |
* a sequence object, depending on the underlying driver. |
* |
* @return string |
* The default value |
*/ |
protected function _resetLastInsertId() |
{ |
return ($this->_lastInsertId = ''); |
} |
/** |
* Updates one or more records |
* |
* @param string|array $tables |
* Table name |
* @param array $updates |
* Associative array of column-value pairs |
* @param array|string $where |
* Only the records matching this condition are updated |
* @return bool |
* @see PDOStatement::execute() |
*/ |
public function update ($tables, array $updates, $where = null) |
{ |
if (!$tables) |
{ |
throw new InvalidArgumentException('No table specified'); |
} |
if (is_array($tables)) |
{ |
$tables = implode(', ', $tables); |
} |
if (!$updates) |
{ |
throw new InvalidArgumentException('No values specified'); |
} |
$params = array(); |
if ($this->_isAssociativeArray($updates)) |
{ |
foreach ($updates as $key => $condition) |
{ |
$params[":{$key}"] = $condition; |
} |
} |
$updates = implode(', ', $this->_escapeValueArray($updates)); |
/* TODO: Should escape table names with escapeName(), but what about aliases? */ |
$query = "UPDATE {$tables} SET {$updates}" . $this->_where($where, '2'); |
$stmt = $this->prepare($query); |
if (is_array($where) && $this->_isAssociativeArray($where)) |
{ |
foreach ($where as $column => $condition) |
{ |
if (is_array($condition) && $this->_isAssociativeArray($condition)) |
{ |
reset($condition); |
$condition = $condition[key($condition)]; |
if (is_array($condition)) |
{ |
foreach (self::_expand($condition, $column) as $param_index => $param_name) |
{ |
$params[$param_name] = $condition[$param_index]; |
} |
} |
} |
else |
{ |
$params[":{$column}2"] = $condition; |
} |
} |
} |
/* DEBUG */ |
if (defined('DEBUG') && DEBUG > 1) |
{ |
debug(array( |
'query' => $query, |
'params' => $params |
)); |
} |
$success =& $this->_lastSuccess; |
$success = $stmt->execute($params); |
$errorInfo =& $this->_lastError; |
$errorInfo = $stmt->errorInfo(); |
$this->_resetLastInsertId(); |
$result =& $this->_lastResult; |
$result = $stmt->fetchAll(); |
if (defined('DEBUG') && DEBUG > 1) |
{ |
debug(array( |
'_lastSuccess' => $success, |
'_lastError' => $errorInfo, |
'_lastResult' => $result |
)); |
} |
return $success; |
} |
/** |
* Inserts a record into a table.<p>The AUTO_INCREMENT value of the inserted |
* row, if any (> 0), is stored in the {@link $lastInsertId} property of |
* the <code>Database</code> instance.</p> |
* |
* @param string $table |
* Table name |
* @param array|string $values |
* Associative array of column-value pairs, indexed array, |
* or comma-separated list of values. If <var>$values</var> is not |
* an associative array, <var>$cols</var> must be passed if the |
* values are not in column order (see below). |
* @param array|string $cols |
* Indexed array, or comma-separated list of column names. |
* Needs only be passed if <var>$values</var> is not an associative array |
* and the values are not in column order (default: <code>null</code>); |
* is ignored otherwise. <strong>You SHOULD NOT rely on column order.</strong> |
* @return bool |
* @see PDOStatement::execute() |
*/ |
public function insert ($table, $values, $cols = null) |
{ |
if ($cols != null) |
{ |
$cols = ' (' |
. (is_array($cols) |
? implode(', ', array_map(array($this, 'escapeName'), $cols)) |
: $cols) . ')'; |
} |
else |
{ |
$cols = ''; |
} |
/* DEBUG */ |
if (defined('DEBUG') && DEBUG > 2) |
{ |
debug(array('values' => $values)); |
} |
$params = array(); |
if (is_array($values)) |
{ |
if ($this->_isAssociativeArray($values)) |
{ |
foreach ($values as $key => $condition) |
{ |
$params[":{$key}"] = $condition; |
} |
$values = $this->_escapeValueArray($values); |
$cols = ''; |
$values = 'SET ' . implode(', ', $values); |
} |
else |
{ |
foreach ($values as &$value) |
{ |
if (is_string($value)) |
{ |
$value = "'" . $value . "'"; |
} |
} |
$values = 'VALUES (' . implode(', ', $values) . ')'; |
} |
} |
/* TODO: Should escape table names with escapeName(), but what about aliases? */ |
$query = "INSERT INTO {$table} {$cols} {$values}"; |
$stmt = $this->prepare($query); |
/* DEBUG */ |
if (defined('DEBUG') && DEBUG > 1) |
{ |
debug(array( |
'query' => $query, |
'params' => $params |
)); |
} |
$success =& $this->_lastSuccess; |
$success = $stmt->execute($params); |
$errorInfo =& $this->_lastError; |
$errorInfo = $stmt->errorInfo(); |
$this->_setLastInsertId(); |
$result =& $this->_lastResult; |
$result = $stmt->fetchAll(); |
if (defined('DEBUG') && DEBUG > 1) |
{ |
debug(array( |
'_lastSuccess' => $success, |
'_lastError' => $errorInfo, |
'_lastInsertId' => $this->_lastInsertId, |
'_lastResult' => $result |
)); |
} |
return $success; |
} |
/** |
* Retrieves all rows from a table |
* |
* @param int[optional] $fetch_style |
* @param int[optional] $column_index |
* @param array[optional] $ctor_args |
* @return array |
* @see PDOStatement::fetchAll() |
*/ |
public function fetchAll($table, $fetch_style = null, $column_index = null, array $ctor_args = null) |
{ |
/* NOTE: Cannot use table name as statement parameter */ |
$stmt = $this->prepare("SELECT * FROM $table"); |
$this->_lastSuccess = $stmt->execute(); |
$this->_lastError = $stmt->errorInfo(); |
$result =& $this->_lastResult; |
if (is_null($fetch_style)) |
{ |
$fetch_style = \PDO::FETCH_ASSOC; |
} |
if (!is_null($ctor_args)) |
{ |
$result = $stmt->fetchAll($fetch_style, $column_index, $ctor_args); |
} |
else if (!is_null($column_index)) |
{ |
$result = $stmt->fetchAll($fetch_style, $column_index); |
} |
else if (!is_null($fetch_style)) |
{ |
$result = $stmt->fetchAll($fetch_style); |
} |
else |
{ |
$result = $stmt->fetchAll(); |
} |
return $result; |
} |
/** |
* Deletes one or more records |
* |
* @param string|array $tables |
* Table name(s) |
* @param array|string $where |
* Only the records matching this condition are deleted |
* @return bool |
* @see PDOStatement::execute() |
*/ |
public function delete($tables, $where = null) |
{ |
if (!$tables) |
{ |
throw new InvalidArgumentException('No table specified'); |
} |
if (is_array($tables)) |
{ |
$tables = implode(', ', $tables); |
} |
$params = array(); |
$query = "DELETE FROM {$tables}" . $this->_where($where); |
$stmt = $this->prepare($query); |
if ($this->_isAssociativeArray($where)) |
{ |
foreach ($where as $column => $condition) |
{ |
if (is_array($condition) && $this->_isAssociativeArray($condition)) |
{ |
reset($condition); |
$condition = $condition[key($condition)]; |
if (is_array($condition)) |
{ |
foreach (self::_expand($condition, $column) as $param_index => $param_name) |
{ |
$params[$param_name] = $condition[$param_index]; |
} |
} |
} |
else |
{ |
$params[":{$column}"] = $condition; |
} |
} |
} |
/* DEBUG */ |
if (defined('DEBUG') && DEBUG > 1) |
{ |
debug(array( |
'query' => $query, |
'params' => $params |
)); |
} |
$success =& $this->_lastSuccess; |
$success = $stmt->execute($params); |
$result =& $this->_lastResult; |
$result = $stmt->fetchAll(); |
$errorInfo =& $this->_lastError; |
$errorInfo = $stmt->errorInfo(); |
if (defined('DEBUG') && DEBUG > 1) |
{ |
debug(array( |
'_lastSuccess' => $success, |
'_lastError' => $errorInfo, |
'_lastResult' => $result |
)); |
} |
return $success; |
} |
} |
/trunk/Db/MySQLDB.php |
---|
0,0 → 1,160 |
<?php |
namespace PointedEars\PHPX\Db; |
class MySQLDB extends Database |
{ |
/** |
* Database host |
* @var string |
*/ |
protected $_host; |
/** |
* Database port on the host |
* @var int |
*/ |
protected $_port; |
/** |
* MySQL Unix socket (shouldn't be used with host or port). |
* @var string |
*/ |
protected $_unix_socket; |
/** |
* Database name |
* @var string |
*/ |
protected $_dbname; |
/** |
* Username to access the database |
* @var string |
*/ |
protected $_username; |
/** |
* Password to access the database |
* @var string |
*/ |
protected $_password; |
/** |
* Optional charset parameter value |
* @var string |
*/ |
protected $_charset = null; |
/** |
* (non-PHPdoc) |
* @see Database::_leftQuote |
*/ |
protected $_leftQuote = '`'; |
/** |
* (non-PHPdoc) |
* @see Database::_rightQuote |
*/ |
protected $_rightQuote = '`'; |
public function __construct() |
{ |
$dbconfig = $this->readConfig(); |
if ($dbconfig === false) |
{ |
return; |
} |
if (isset($dbconfig['unix_socket'])) |
{ |
$this->_unix_socket = $dbconfig['unix_socket']; |
} |
$this->_dsn = "mysql:host={$this->_host}" |
. (!is_null($this->_port) ? ";port={$this->_port}" : '') |
. (!is_null($this->_unix_socket) ? ";unix_socket={$this->_unix_socket}" : '') |
. (!is_null($this->_dbname) ? ";dbname={$this->_dbname}" : '') |
. (!is_null($this->_charset) ? ";charset={$this->_charset}" : ''); |
if (!is_null($this->_charset)) |
{ |
$this->_options[\PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES " . $this->_charset; |
} |
parent::__construct(); |
} |
/** |
* (non-PHPdoc) |
* @see \PointedEars\PHPX\Db\Database::_columnDef() |
*/ |
protected function _columnDef (array $data, $columnName) |
{ |
$standard_Def = parent::_columnDef($data, $columnName); |
$mySQL_def = $standardDef |
. (isset($data['format']) && $data['format'] ? " COLUMN_FORMAT {$data['format']}" : '') |
. (isset($data['storage']) && $data['storage'] ? " STORAGE {$data['storage']}" : ''); |
return $mySQL_def; |
} |
/** |
* Creates this MySQL database |
* |
* @param string $dsn |
* Ignored. Required for strict compatibility only. |
* @param string $username = null |
* @param string $password = null |
* @param array? $options = null |
* Ignored. Required for strict compatibility only. |
* @param string $dbspec = null |
* Currently ignored. |
* @param boolean $force = false |
* @see Database::create() |
*/ |
public function create ($dsn, $username = null, $password = null, |
array $options = null, $dbspec = null, $force = false) |
{ |
return parent::create( |
"mysql:host={$this->_host}" |
. (!is_null($this->_charset) ? ";charset={$this->_charset}" : ''), |
$username, $password, null, $force); |
} |
/** |
* (non-PHPdoc) |
* @see \PointedEars\PHPX\Db\Database::getLastModified() |
*/ |
public function getLastModified ($table = null) |
{ |
$filter = array('TABLE_SCHEMA' => $this->_dbname); |
if ($table !== null) |
{ |
$filter['TABLE_NAME'] = $table; |
} |
$times = $this->select( |
'`information_schema`.`tables`', |
array('CREATE_TIME', 'UPDATE_TIME'), |
$filter, |
array('UPDATE_TIME DESC', 'CREATE_TIME DESC'), |
1 |
); |
$modi = ($times ? $times[0] : null); |
if ($modi) |
{ |
$modi = $modi['UPDATE_TIME'] ?: $modi['CREATE_TIME']; |
if ($modi) |
{ |
$modi = strtotime($modi . ' GMT'); |
} |
} |
return $modi; |
} |
} |
/trunk/Db/Table.php |
---|
0,0 → 1,401 |
<?php |
namespace PointedEars\PHPX\Db; |
use \PointedEars\PHPX\Application; |
/** |
* Generic database table model class |
* |
* @author Thomas Lahn |
* @property Database $database |
* @property-read int $lastInsertId |
* ID of the last inserted row, or the last value from |
a sequence object, depending on the underlying driver. |
*/ |
class Table extends \PointedEars\PHPX\AbstractModel |
{ |
/** |
* Name of the table |
* @var string |
*/ |
protected static $_name = ''; |
/** |
* Columns definition |
* @var array |
* @see Table::create() |
*/ |
protected static $_columns; |
/** |
* Indexes definition |
* @var array |
* @see Table::create() |
*/ |
protected static $_indexes; |
/** |
* Constraints definition |
* @var array |
* @see Table::create() |
*/ |
protected static $_constraints; |
/** |
* Database of the table |
* @var Database|string |
* @see Table::create() |
*/ |
protected static $_database; |
/** |
* Name of the primary key column of the table |
* @var string |
*/ |
protected static $_id = 'id'; |
/** |
* Creates a new <code>Table</code> instance. |
* |
* Each of the parameters is optional and can also be given |
* by a protected property where the parameter name is preceded |
* by <code>_</code>. Parameter values overwrite the default |
* property values. It is recommended to use default property |
* values of inheriting classes except for small applications |
* and testing purposes. |
* |
* @param Database $database |
* Database of the table (required in order to use a fitting |
* query language) |
* @param string $name |
* Table name |
* @param string $id |
* Name of the primary key column |
* @throws InvalidArgumentException |
*/ |
public function __construct ($database = null, $name = '', $id = '') |
{ |
if ($database === null) |
{ |
/* Call getter to convert to Database if possible */ |
if ($this->database === null) |
{ |
$this->database = Application::getInstance()->getDefaultDatabase(); |
} |
} |
else |
{ |
$this->database = $database; |
} |
if ($name !== '') |
{ |
$this->name = $name; |
} |
$name = $this->name; |
if (!\is_string($name)) |
{ |
throw new \InvalidArgumentException( |
'Expected string for table name, saw ' |
. (\is_object($name) ? \get_class($name) : \gettype($name))); |
} |
if ($id !== '') |
{ |
$this->id = $id; |
} |
} |
/** |
* @param string $value |
*/ |
public function setName ($value) |
{ |
$class = \get_class($this); |
$class::$_name = (string) $value; |
} |
/** |
* @return string |
*/ |
public function getName () |
{ |
$class = \get_class($this); |
return $class::$_name; |
} |
/** |
* Returns the database for the table |
* @return Database |
*/ |
public function getDatabase() |
{ |
/* FIXME: What about tables from different databases? */ |
$class = \get_class($this); |
if (\is_string($class::$_database)) |
{ |
/* Call setter to convert to Database */ |
$this->setDatabase($class::$_database); |
} |
return $class::$_database; |
} |
/** |
* @param Database|string $value |
* @throws InvalidArgumentException |
*/ |
public function setDatabase ($value) |
{ |
$class = \get_class($this); |
if ($value instanceof Database) |
{ |
$class::$_database = $value; |
} |
else if ($value !== null) |
{ |
$database = new $value(); |
if (!($database instanceof Database)) |
{ |
throw new \InvalidArgumentException( |
'Expected Database instance or string for class name, saw ' |
. (\is_object($value) ? \get_class($value) : \gettype($value)) |
); |
} |
$class::$_database = $database; |
} |
} |
/** |
* @param string $value |
*/ |
public function setId ($value) |
{ |
$class = \get_class($this); |
$class::$_id = (string) $value; |
} |
/** |
* @return string |
*/ |
public function getId () |
{ |
$class = \get_class($this); |
return $class::$_id; |
} |
/** |
* Returns the <var>options</var> array for {@link Database::createTable} |
* |
* Should be called and overridden by inheriting classes. |
* |
* @return array |
*/ |
protected function _createOptions () |
{ |
$options = array(); |
foreach (array('indexes', 'constraints') as $option) |
{ |
if ($class::${"_$option"}) |
{ |
$options[$option] = $class::${"_$option"}; |
} |
} |
return $options; |
} |
/** |
* Creates the table for this model |
* |
* @return bool |
*/ |
public function create () |
{ |
$class = \get_class($this); |
return $this->database->createTable( |
$class::$_name, $class::$_columns, $this->_createOptions()); |
} |
/** |
* Initiates a transaction |
* |
* @return bool |
* @see Database::beginTransaction() |
*/ |
public function beginTransaction() |
{ |
return $this->database->beginTransaction(); |
} |
/** |
* Rolls back a transaction |
* |
* @return bool |
* @see Database::rollBack() |
*/ |
public function rollBack() |
{ |
return $this->database->rollBack(); |
} |
/** |
* Commits a transaction |
* |
* @return bool |
* @see Database::commit() |
*/ |
public function commit() |
{ |
return $this->database->commit(); |
} |
/** |
* Retrieves all rows from the table |
* |
* @return array |
* @see Database::fetchAll() |
*/ |
public function fetchAll($fetch_style = null, $column_index = null, array $ctor_args = null) |
{ |
return $this->database->fetchAll($this->name, $fetch_style, $column_index, $ctor_args); |
} |
/** |
* Selects data from one or more tables |
* |
* @return array |
* @see Database::select() |
*/ |
public function select($columns = null, $where = null, $order = null, $limit = null) |
{ |
return $this->database->select($this->name, $columns, $where, $order, $limit); |
} |
/** |
* Updates records in one or more tables |
* |
* @return bool |
* @see Database::update() |
*/ |
public function update($data, $condition) |
{ |
return $this->database->update($this->name, $data, $condition); |
} |
/** |
* Inserts a record into the table |
* |
* @return bool |
* @see Database::insert() |
*/ |
public function insert($data, $cols = null) |
{ |
return $this->database->insert($this->name, $data, $cols); |
} |
/** |
* Returns the ID of the last inserted row, or the last value from |
* a sequence object, depending on the underlying driver. |
* |
* @return int |
* @see Database::getLastInsertId() |
*/ |
public function getLastInsertId() |
{ |
return $this->database->lastInsertId; |
} |
/** |
* Delete a record from the table |
* |
* @param int $id |
* ID of the record to delete. May be <code>null</code>, |
* in which case <var>$condition</var> must specify |
* the records to be deleted. |
* @param array[optional] $condition |
* Conditions that must be met for a record to be deleted. |
* Ignored if <var>$id</var> is not <code>null</code>. |
* @return bool |
* @throws InvalidArgumentException if both <var>$id</var> and |
* <var>$condition</var> are <code>null</code>. |
* @see Database::delete() |
*/ |
public function delete ($id, array $condition = null) |
{ |
if ($id !== null) |
{ |
$condition = array($this->id => $id); |
} |
else if ($condition === null) |
{ |
throw new InvalidArgumentException( |
'$id and $condition cannot both be null'); |
} |
return $this->database->delete($this->name, $condition); |
} |
/** |
* Inserts a row into the table or updates an existing one |
* |
* @param array $data |
* Associative array of column-value pairs to be updated/inserted |
* @param string|array $condition |
* If there are no records matching this condition, a row |
* will be inserted; otherwise matching records are updated. |
* @return bool |
* The return value of Table::update() or Table::insert() |
* @see Table::update() |
* @see Table::insert() |
*/ |
public function updateOrInsert($data, array $condition = null) |
{ |
if ($this->select($this->id, $condition)) |
{ |
return $this->update($data, $condition); |
} |
return $this->insert($data); |
} |
/** |
* Finds a record by ID |
* |
* @param mixed $id |
* @return array |
*/ |
public function find ($id) |
{ |
/* DEBUG */ |
if (defined('DEBUG') && DEBUG > 0) |
{ |
debug($id); |
} |
$result = $this->select(null, array($this->id => $id)); |
if ($result) |
{ |
$result = $result[0]; |
} |
return $result; |
} |
/** |
* Returns the date of last modification of this table. |
* |
* @return int|null |
* Timestamp of last modification, or <code>null</code> if |
* unavailable. |
*/ |
public function getLastModified () |
{ |
return $this->database->getLastModified($this->name); |
} |
} |
/trunk/Db/Mapper.php |
---|
0,0 → 1,100 |
<?php |
namespace PointedEars\PHPX\Db; |
/** |
* Generic abstract database mapper class |
* |
* @property-read \PointedEars\PHPX\Db\Table $table |
* The <code>Table</code> for this mapper |
* @author Thomas Lahn |
*/ |
abstract class Mapper extends \PointedEars\PHPX\AbstractModel |
{ |
/** |
* Class name of the associated table model |
* |
* @var string |
*/ |
protected $_table = 'Table'; |
protected $_dbTable; |
/** |
* Sets the {@link Table} for this mapper |
* @param string|Table $dbTable |
* Class name of the new instance, or an existing instance |
* @throws Exception if <var>$dbTable</var> is not a <code>Table</code> |
*/ |
public function setDbTable($table) |
{ |
if (is_string($table)) |
{ |
$table = new $table(); |
} |
if (!($table instanceof Table)) { |
throw new Exception('Invalid table data gateway provided'); |
} |
$this->_dbTable = $table; |
} |
/** |
* Gets the {@link Table} for this mapper |
* |
* @param string|Table $table |
* Class name of the new instance or an existing instance. |
* The default is the value of the <code>$_table</code> property. |
* @return Table |
* @throws Exception if <var>$dbTable</var> is not a <code>Table</code> |
* @see Mapper::setDbTable() |
*/ |
public function getDbTable($table = null) |
{ |
if (is_null($this->_dbTable)) |
{ |
if (is_null($table)) |
{ |
$table = $this->_table; |
} |
$this->setDbTable($table); |
} |
return $this->_dbTable; |
} |
/** |
* Returns the <code>Table</code> for this object. |
* |
* @return Table |
*/ |
public function getTable () |
{ |
return $this->getDbTable(); |
} |
/** |
* Sorts an array of objects by the property of the nested object. |
* To be used with the u*sort() functions. |
* |
* @param object $a First operand of the comparison |
* @param object $b Second operand of the comparison |
* @param string $property |
* Name of the property of the nested object by which to sort the outer array |
* @return int |
* 0 if <var>$a</var> and <var>$b</var> are "equal", |
* <code>-1</code> if <var>$a</var> is "less" than <var>$b</var>, |
* <code>1</code> otherwise. |
*/ |
protected static function sortByProperty($a, $b, $property) |
{ |
if ($a->$property === $b->$property) |
{ |
return 0; |
} |
return ($a->$property < $b->$property) ? -1 : 1; |
} |
} |
/trunk/Db/MySQLTable.php |
---|
0,0 → 1,38 |
<?php |
namespace PointedEars\PHPX\Db; |
/** |
* @author |
* Copyright (c) 2013 Thomas 'PointedEars' Lahn |
*/ |
class MySQLTable extends Table |
{ |
/** |
* Engine for this table. |
* |
* @var string |
* @see Table::create() |
*/ |
protected static $_engine; |
/** |
* (non-PHPdoc) |
* @see \PointedEars\PHPX\Db\Table::_createOptions() |
*/ |
protected function _createOptions () |
{ |
$options = parent::_createOptions(); |
$class = \get_class($this); |
foreach (array('engine') as $option) |
{ |
if ($class::${"_$option"}) |
{ |
$options[$option] = $class::${"_$option"}; |
} |
} |
return $options; |
} |
} |
/trunk/Db/database.class.php |
---|
0,0 → 1,527 |
<?php |
require_once __DIR__ . '/../global.inc'; |
/* NOTE: Obsolete with autoloader */ |
//require_once 'Zend/Registry.php'; |
//require_once 'Zend/Db/Table.php'; |
/** |
* @property-read array $lastId The last generated ID |
* @property-read array $result The result of the last query |
* |
* @author rvejseli, tlahn |
*/ |
class Database |
{ |
private $db_host = DB_HOST; |
private $db_user = DB_USER; |
private $db_pass = DB_PASS; |
private $db_name = DB_NAME; |
private $connection = false; |
private $_result = array(); |
/** |
* The last inserted ID |
* |
* @var int |
*/ |
private $_lastId; |
/* Anlegen der Instanz */ |
private static $instance = NULL; |
/* Konstruktor private, damit die Klasse nur aus sich selbst heraus instanziiert werden kann. */ |
private function __construct() |
{ |
$this->connect(); |
$this->_resetLastID(); |
} |
/** |
* Returns a singleton Database instance |
* @return Database |
*/ |
public static function getInstance() |
{ |
if (self::$instance === NULL) |
{ |
self::$instance = new self; |
} |
return self::$instance; |
} |
/* Klonen per 'clone()' von aussen verbieten. */ |
private function __clone() {} |
/** |
* Universal getter |
* |
* @param string $name |
*/ |
public function __get($name) |
{ |
switch ($name) |
{ |
case 'lastId': |
case 'result': |
return $this->{"_$name"}; |
} |
} |
/** |
* Connects to the Database |
*/ |
public function connect() |
{ |
if (!$this->connection) |
{ |
$myconn = @mysql_connect($this->db_host, $this->db_user, $this->db_pass); |
if ($myconn) |
{ |
$seldb = @mysql_select_db($this->db_name, $myconn); |
if ($seldb) |
{ |
$this->connection = true; |
return true; |
} |
else |
{ |
return false; |
} |
} |
else |
{ |
return false; |
} |
} |
else |
{ |
return true; |
} |
} |
/** |
* Resets the last ID to a default value, indicating that no rows where inserted |
*/ |
private function _resetLastID() |
{ |
$this->_lastId = -1; |
} |
public function setDatabase($name) |
{ |
if ($this->connection) |
{ |
if (@mysql_close()) |
{ |
$this->connection = false; |
$this->_result = null; |
$this->db_name = $name; |
$this->connect(); |
} |
} |
} |
/** |
* Makes an encoding-safe database query |
* |
* @param string $query |
* @param string encoding |
* The encoding in which the query result should be returned from the DBMS. |
* The default is 'utf8'. |
* @return resource|null |
* The query result if successful, <code>null</code> otherwise |
*/ |
private function _query($query, $encoding = 'utf8') |
{ |
$this->_resetLastID(); |
if (mysql_query("SET CHARACTER SET $encoding")) |
{ |
if (DEBUG > 1) |
{ |
echo "<pre>$query</pre>"; |
} |
return @mysql_query($query); |
} |
else |
{ |
/* DEBUG */ |
echo mysql_errno() . ':' . mysql_error(); |
exit; |
} |
return null; |
} |
private function _tableExists($table) |
{ |
$tablesInDb = $this->_query("SHOW TABLES FROM {$this->db_name} LIKE '{$table}'"); |
if ($tablesInDb) |
{ |
if (mysql_num_rows($tablesInDb) == 1) |
{ |
return true; |
} |
else |
{ |
return false; |
} |
} |
} |
/** |
* Determines if an array is associative (does not have a '0' key) |
* |
* @param array $a |
* @return boolean |
* <code>true</code> if <var>$a</var> is associative, |
* <code>false</code> otherwise |
*/ |
private function _isAssociativeArray(array $a) |
{ |
return !array_key_exists(0, $a); |
} |
/** |
* Escapes an associative array so that its string representation can be used |
* in a query. |
* |
* NOTE: Intentionally does not check whether the array actually is associative! |
* |
* @param array &$array |
* The array to be escaped |
* @return array |
* The escaped array |
*/ |
private function _escapeArray(array &$array) |
{ |
foreach ($array as $column => &$value) |
{ |
if (is_string($value)) |
{ |
$value = mysql_real_escape_string($value); |
} |
$value = "`" . mysql_real_escape_string($column) . "`='{$value}'"; |
} |
return $array; |
} |
/** |
* Constructs the WHERE part of a query |
* |
* @param string|array $where Condition |
* @return string |
*/ |
private function _where($where) |
{ |
if (!is_null($where)) |
{ |
if (is_array($where)) |
{ |
if (count($where) < 1) |
{ |
return ''; |
} |
if ($this->_isAssociativeArray($where)) |
{ |
$this->_escapeArray($where); |
} |
$where = '(' . join(') AND (', $where) . ')'; |
} |
return ' WHERE ' . $where; |
} |
return ''; |
} |
/** |
* Selects data from one or more tables; the resulting records are stored |
* in the <code>result</code> property. |
* |
* @param string|array[string] $tables Table(s) to select from |
* @param string|array[string] $columns Column(s) to select from (optional) |
* @param string|array $where Condition (optional) |
* @param string $order Sort order (optional) |
* @param string $limit Limit (optional) |
* @return bool |
* @throws <code>Exception</code> if the query fails |
*/ |
public function select($tables, $columns = null, $where = null, $order = null, $limit = null) |
{ |
$this->_result = array(); |
if (is_null($columns)) |
{ |
$columns = array('*'); |
} |
if (!is_array($columns)) |
{ |
$columns = array($columns); |
} |
$columns = join(',', $columns); |
if (!is_array($tables)) |
{ |
$tables = array($tables); |
} |
$tables = join(',', $tables); |
$q = "SELECT $columns FROM $tables" . $this->_where($where); |
if (!is_null($order)) |
{ |
$q .= " ORDER BY $order"; |
} |
if (!is_null($limit)) |
{ |
$q .= " LIMIT $limit"; |
} |
if (DEBUG > 1) |
{ |
echo "<pre>$q</pre>"; |
} |
/* DEBUG */ |
// debug($q); |
$_result = $this->_query($q); |
if ($_result) |
{ |
$this->numResults = mysql_num_rows($_result); |
for ($i = 0, $len = $this->numResults; $i < $len; ++$i) |
{ |
$_results = mysql_fetch_array($_result); |
/* DEBUG */ |
// debug($_results); |
$keys = array_keys($_results); |
foreach ($keys as $key) |
{ |
if (!is_int($key)) |
{ |
if (mysql_num_rows($_result) > 0) |
{ |
$this->_result[$i][$key] = $_results[$key]; |
} |
else if (mysql_num_rows($_result) < 1) |
{ |
$this->_result = null; |
} |
} |
} |
} |
return $q; |
} |
else |
{ |
throw new Exception(mysql_error() . ". Query: " . $q); |
} |
} |
/** |
* Inserts a record into a table.<p>The AUTO_INCREMENT value of the inserted |
* row, if any (> 0), is stored in the {@link $lastId} property of |
* the <code>Database</code> instance.</p> |
* |
* @param string $table |
* Table name |
* @param array|string $values |
* Associative array of column-value pairs, indexed array, |
* or comma-separated list of values. If <var>$values</var> is not |
* an associative array, <var>$cols</var> must be passed if the |
* values are not in column order (see below). |
* @param array|string $cols |
* Indexed array, or comma-separated list of column names. |
* Needs only be passed if <var>$values</var> is not an associative array |
* and the values are not in column order (default: <code>null</code>); |
* is ignored otherwise. <strong>You SHOULD NOT rely on column order.</strong> |
* @return bool |
* <code>true</code> if successful, <code>false</code> otherwise |
*/ |
public function insert($table, $values, $cols = null) |
{ |
if ($cols != null) |
{ |
$cols = ' (' |
. (is_array($cols) |
? join(',', array_map(create_function('$s', 'return "`$s`";'), $cols)) |
: $cols) . ')'; |
} |
else |
{ |
$cols = ''; |
} |
/* DEBUG */ |
// debug($values); |
if ($this->_isAssociativeArray($values)) |
{ |
$this->_escapeArray($values); |
$cols = ''; |
$values = 'SET ' . join(', ', $values); |
} |
else |
{ |
foreach ($values as &$value) |
{ |
if (is_string($value)) |
{ |
$value = "'" . mysql_real_escape_string($value) . "'"; |
} |
} |
$values = ' VALUES (' . join(', ', $values) . ')'; |
} |
$insert = "INSERT INTO `{$table}` {$cols} {$values}"; |
/* DEBUG */ |
// echo "Insert:<br>"; |
// debug($insert); |
$ins = $this->_query($insert); |
$this->_lastId = mysql_insert_id(); |
if ($ins) |
{ |
return true; |
} |
else |
{ |
return false; |
} |
} |
/** |
* Deletes records from a table |
* |
* @param string $table |
* @param string|array|null $where Condition for deletion |
* @return bool |
*/ |
public function delete($table, $where = null) |
{ |
$delete = "DELETE FROM `{$table}`" . $this->_where($where); |
/* DEBUG */ |
// debug($delete); |
// $result = true; |
$result = $this->_query($delete); |
return ($result) ? true : false; |
} |
/** |
* Updates one or more records |
* |
* @param string|array $tables |
* Table name |
* @param array $values |
* Associative array of column-value pairs |
* @param array|string $where |
* Only the records matching this condition are updated |
* @return string|bool |
*/ |
public function update($tables, $updates, $where = null) |
{ |
if (!$tables) |
{ |
throw new InvalidArgumentException('No table specified'); |
} |
if (is_array($tables)) |
{ |
$tables = join(',', $tables); |
} |
if (!$updates) |
{ |
throw new InvalidArgumentException('No values specified'); |
} |
$updates = join(',', $this->_escapeArray($updates)); |
$query = "UPDATE {$tables} SET {$updates}" . $this->_where($where); |
/* DEBUG */ |
// echo "Update:<br>"; |
// debug($query); |
$_result = $this->_query($query); |
if (!$_result) |
{ |
//debug(mysql_error()); |
return false; |
} |
return $query; |
} |
/** |
* Inserts a row into a table or updates an existing one |
* |
* @param string $sTable |
* Table name |
* @param array $aValues |
* Associative array of column-value pairs to be updated/inserted |
* @param string|array $condition |
* If there are no records matching this condition, a row will be inserted; |
* otherwise matching records are updated |
* @return mixed |
*/ |
public function updateOrInsert($sTable, $aValues, $condition) |
{ |
$result = $this->select($sTable, '*', $condition); |
if (!$result) |
{ |
exit; |
} |
// debug($this->result); |
if (count($this->result) > 0) |
{ |
$result = $this->update($sTable, $aValues, $condition); |
} |
else |
{ |
$result = $this->insert($sTable, $aValues); |
} |
return $result; |
} |
/** |
* @see Database::updateOrInsert() |
*/ |
public function insertOrUpdate($sTable, $aValues, $condition) |
{ |
return $this->updateOrInsert($sTable, $aValues, $condition); |
} |
public function getResult() |
{ |
return $this->result; |
} |
} |
?> |
/trunk/Db/ODBCDB.php |
---|
0,0 → 1,31 |
<?php |
require_once __DIR__ . '/Database.php'; |
class ODBCDB extends Database |
{ |
/** |
* ODBC alias |
* @var string |
*/ |
protected $_alias; |
/** |
* (non-PHPdoc) |
* @see Database::_leftQuote |
*/ |
protected $_leftQuote = '['; |
/** |
* (non-PHPdoc) |
* @see Database::_rightQuote |
*/ |
protected $_rightQuote = ']'; |
public function __construct() |
{ |
// $this->_connection = @odbc_connect($this->_alias, $this->_username, $this->_password); |
$this->_dsn = 'odbc:' . $this->_alias; |
parent::__construct(); |
} |
} |
/trunk/Db/Adapter.php |
---|
0,0 → 1,101 |
<?php |
require_once __DIR__ . '/../Model.php'; |
abstract class Adapter |
{ |
/** |
* Database used by the adapter |
* @var Database |
*/ |
protected $_database = null; |
/** |
* Constructs the adapter, associating a {@link Database} with it |
* @param Database $database |
*/ |
/* Singleton */ |
protected function __construct(Database $database) |
{ |
$this->_database = $database; |
} |
/** |
* Selects data from one or more tables |
* |
* @return array |
* @see Database::select() |
*/ |
public function select($table, $columns = null, $where = null, $order = null, $limit = null) |
{ |
return $this->_database->select($table, $columns, $where, $order, $limit); |
} |
/** |
* Finds all records matching the set properties of a model object |
* |
* @param Model $object |
* @return array[Model] |
*/ |
public function findAll(Model $object, $order = null, $limit = null) |
{ |
$properties = $object->getPropertyVars(); |
$where = array(); |
foreach ($properties as $property) |
{ |
if (!is_null($object->$property)) |
{ |
$where[$property] = $object->$property; |
} |
} |
$class = get_class($object); |
$query_result = $this->select($class::persistentTable, null, $where, $order, $limit); |
$num_results = count($query_result); |
if ($num_results === 0) |
{ |
return null; |
} |
$result = array(); |
foreach ($query_result as $row) |
{ |
$result[] = new $class($row); |
} |
return $result; |
} |
/** |
* Finds the record for a model object by its primary key |
* |
* @param Model $object |
* @return Model |
* The filled object if the primary key value could be found, |
* <code>null</code> otherwise |
*/ |
public function find(Model $object) |
{ |
$class = get_class($object); |
$primaryKey = $class::persistentPrimaryKey; |
if (is_array($primaryKey)) |
{ |
/* TODO */ |
} |
$result = $this->select($class::persistentTable, null, array($primaryKey => $object->$primaryKey)); |
if (0 == count($result)) |
{ |
return null; |
} |
$row = $result[0]; |
$object->map($row); |
return $object; |
} |
} |
/trunk/Db/MySQLAdapter.php |
---|
0,0 → 1,17 |
<?php |
require_once __DIR__ . '/Adapter.php'; |
require_once __DIR__ . '/MySQLDB.php'; |
class MySQLAdapter extends Adapter |
{ |
/** |
* Constructs the adapter, associating a {@link MySQLDB} with it |
* @param MySQLDB $database |
*/ |
/* Singleton */ |
protected function __construct(MySQLDB $database) |
{ |
parent::__construct($database); |
} |
} |
/trunk/Db/AccessDB.php |
---|
0,0 → 1,33 |
<?php |
require_once __DIR__ . '/ODBCDB.php'; |
class AccessDB extends ODBCDB |
{ |
/** |
* Escapes a database name so that it can be used in a query. |
* |
* @param string $name |
* The name to be escaped |
* @return string |
* The escaped name |
*/ |
public function escapeName($name) |
{ |
return '[' . $name . ']'; |
} |
/** |
* (non-PHPdoc) |
* @see Database::_escapeAliasArray() |
*/ |
protected function _escapeAliasArray(array &$array) |
{ |
foreach ($array as $column => &$value) |
{ |
$value = $value . ' AS [' . $column . ']'; |
} |
return $array; |
} |
} |
/trunk/Application.php |
---|
0,0 → 1,329 |
<?php |
namespace PointedEars\PHPX; |
if (!defined('\\DIRECTORY_SEPARATOR')) |
{ |
define('DIRECTORY_SEPARATOR', '/'); |
} |
function autoload ($class) |
{ |
if (\strpos($class, '..') !== false) |
{ |
throw new \InvalidArgumentException( |
"Refusing to load unsafe class '{$class}'"); |
} |
require_once \str_replace('\\', DIRECTORY_SEPARATOR, |
\preg_replace( |
'#^' . \preg_quote(__NAMESPACE__, '#') .'#', |
__DIR__, |
$class |
) |
) . '.php'; |
} |
\spl_autoload_register(__NAMESPACE__ . '\\autoload'); |
/** |
* Basic application class |
* |
* @author Thomas Lahn |
*/ |
class Application |
{ |
/** |
* Relative path to the controllers directory |
* @var string |
*/ |
protected $_controllerPath = 'controllers'; |
/** |
* Default controller of the application |
* @var string |
*/ |
protected $_defaultController = 'Index'; |
/** |
* Registry key for the default database of the application |
* @var string |
*/ |
protected $_defaultDatabase; |
/** |
* Currently active controller of this application |
* @var Controller |
*/ |
protected $_currentController; |
/** |
* Singleton |
* |
* @var Application |
*/ |
private static $_instance; |
protected function __construct () |
{ |
/* Singleton pattern */ |
} |
/** |
* Gets a reference to the <code>Application</code> instance |
* |
* @param Application $instance |
* The instance to be used as application. The default is a new |
* application. This parameter is ignored if the application was |
* already initialized. |
* @return Application |
*/ |
public static function getInstance (Application $instance = null) |
{ |
if (self::$_instance === null) |
{ |
self::$_instance = ($instance === null) ? new self() : $instance; |
} |
return self::$_instance; |
} |
/** |
* Getter for properties |
* |
* @param string $name |
* @throws ModelPropertyException |
* @return mixed |
*/ |
public function __get ($name) |
{ |
/* Support for Object-Relational Mappers */ |
if (\strpos($name, 'persistent') === 0) |
{ |
$class = \get_class($this); |
return $class::${$name}; |
} |
$method = 'get' . \ucfirst($name); |
if (\method_exists($this, $method)) |
{ |
return $this->$method(); |
} |
if (\property_exists($this, "_$name")) |
{ |
return $this->{"_$name"}; |
} |
return $this->$name; |
} |
/** |
* Setter for properties |
* |
* @param string $name |
* @param mixed $value The new property value before assignment |
* @throws ModelPropertyException |
*/ |
public function __set ($name, $value) |
{ |
$method = 'set' . \ucfirst($name); |
if (\method_exists($this, $method)) |
{ |
return $this->$method($value); |
} |
if (\property_exists($this, "_$name")) |
{ |
$this->{"_$name"} = $value; |
return $this->{"_$name"}; |
} |
/* NOTE: Attempts to set other properties are _silently_ _ignored_ */ |
} |
/** |
* Runs the application, setting up session management and |
* constructing the controller indicated by the URI |
*/ |
public function run () |
{ |
$this->startSession(); |
$controller = self::getParam('controller', $_REQUEST); |
if (!$controller) |
{ |
$controller = $this->_defaultController; |
} |
$controller = \ucfirst($controller); |
$controller = $controller . 'Controller'; |
require_once "{$this->_controllerPath}/{$controller}.php"; |
$this->_currentController = new $controller(); |
return $this; |
} |
protected function startSession () |
{ |
\session_start(); |
} |
/** |
* Gets a request parameter |
* |
* @param string $key |
* Key to look up in the array |
* @param array $array |
* Array where to look up <var>$key</var>. |
* The default is <code>$_GET</code>. |
* @return mixed |
* <code>null</code> if there is no such <var>$key</var> |
* in <var>$array</var> |
*/ |
public static function getParam ($key, array $array = null) |
{ |
if ($array === null) |
{ |
$array = $_GET; |
} |
return isset($array[$key]) ? $array[$key] : null; |
} |
/** |
* Registers a database |
* |
* @param string $key |
* @param Database $database |
* @return string Registry key |
* @see Application::setDefaultDatabase() |
*/ |
public function registerDatabase ($key, Db\Database $database) |
{ |
Registry::set($key, $database); |
return $key; |
} |
/** |
* Sets the default database |
* @param string Registry key to refer to the {@link Database} |
*/ |
public function setDefaultDatabase ($key) |
{ |
$this->_defaultDatabase = $key; |
} |
/** |
* Sets the current controller for this application |
* |
* @param Controller $controller |
* @return Application |
*/ |
public function setCurrentController (Controller $controller) |
{ |
$this->_currentController = $controller; |
return $this; |
} |
/** |
* Returns the current controller for this application |
* |
* @return Controller |
*/ |
public function getCurrentController () |
{ |
return $this->_currentController; |
} |
/** |
* Returns the default database for this application |
* |
* @return Database |
*/ |
public function getDefaultDatabase () |
{ |
return Registry::get($this->_defaultDatabase); |
} |
/** |
* Returns a relative URI-reference for an action of the |
* application |
* |
* @param string[optional] $controller |
* @param string[optional] $action |
* @param int[optional] $id |
*/ |
public function getURL ($controller = null, $action = null, $id = null) |
{ |
/* Apache module */ |
$url = self::getParam('SCRIPT_URL', $_SERVER); |
if ($url === null) |
{ |
/* FastCGI */ |
$url = self::getParam('URL', $_SERVER); |
if ($url === null) |
{ |
/* Server/PHP too old, compute URI */ |
$url = self::getParam('REQUEST_URI', $_SERVER); |
if (\preg_match('/^[^?]+/', $url, $matches) > 0) |
{ |
$url = $matches[0]; |
} |
else |
{ |
/* Has .php in it, but at least it works */ |
$url = self::getParam('SCRIPT_NAME', $_SERVER); |
if ($url === null) |
{ |
throw new \Exception( |
'None of $_SERVER["SCRIPT_URL"], $_SERVER["URL"],' |
. ' $_SERVER["REQUEST_URI"], or $_SERVER["SCRIPT_NAME"]' |
. ' is available, cannot continue.'); |
} |
} |
} |
} |
$query = (($controller !== null) ? 'controller=' . $controller : '') |
. (($action !== null) ? '&action=' . $action : '') |
. (($id !== null) ? '&id=' . $id : ''); |
return $url . ($query ? '?' . $query : ''); |
} |
/** |
* Performs a server-side redirect within the application |
*/ |
public static function redirect ($query = '') |
{ |
$script_uri = self::getParam('SCRIPT_URI', $_SERVER); |
if ($script_uri === null) |
{ |
/* Server/PHP too old, compute URI */ |
if (\preg_match('/^[^?]+/', |
self::getParam('REQUEST_URI', $_SERVER), $matches) > 0) |
{ |
$query_prefix = $matches[0]; |
} |
else |
{ |
/* Has .php in it, but at least it works */ |
$query_prefix = self::getParam('SCRIPT_NAME', $_SERVER); |
} |
/* TODO: Let user decide which ports map to which URI scheme */ |
$script_uri = (self::getParam('SERVER_PORT', $_SERVER) == 443 |
? 'https://' |
: 'http://') |
. self::getParam('HTTP_HOST', $_SERVER) |
. $query_prefix; |
} |
\header('Location: ' . $script_uri |
. ($query ? (substr($query, 0, 1) === '?' ? '' : '?') . $query : '')); |
} |
} |
/trunk/StrictModel.php |
---|
0,0 → 1,13 |
<?php |
namespace PointedEars\PHPX; |
/** |
* Abstract model class for strict Object-Relational Mapping |
* |
* @author Thomas Lahn |
*/ |
abstract class StrictModel extends \PointedEars\PHPX\Model |
{ |
protected static $_strict = true; |
} |
/trunk/Base.php |
---|
0,0 → 1,133 |
<?php |
namespace PointedEars\PHPX; |
/** |
* Base class providing generic wrappers for reading from and |
* and writing to inaccessible properties. |
* |
* For each such property there must exist a public dynamically |
* bound method (the PHP default) in the inheriting class whose |
* name is prefixed with 'get' (a <i>getter</i>, for read access) |
* and/or with 'set' (a <i>setter</i>, for write access) followed |
* by the property name. (It is recommended to write the first |
* letter of the property name in the method name in uppercase. |
* For runtime-efficiency, underscores in the property name are |
* <em>not</em> converted to camel-case ["property_name" requires |
* "getProperty_name", <em>not</em> "getPropertyName"].) |
* |
* The getter or setter would then access the non-public property |
* whose name is the name of the accessed property prefixed with |
* underscore ('_'), called the <em>underscore property</em>. |
* It can make sure that the value of the underscore property is |
* of a specific type or within a specific range, i. e. perform |
* automatic type conversion, normalize values that are out of |
* range when the property is read, and reject/ignore attempts |
* to set unsuitable property values. This is particularly useful |
* with instances of model classes; see {@link AbstractModel} and |
* {@link Model}. |
* |
* Properties that do not have a getter are not available |
* from unprivileged context, and an exception is thrown |
* when attempting to read from them there. |
* |
* Properties that do not have a setter are effectively |
* <em>read-only</em> from unprivileged context, and |
* an exception is thrown when attempting to write to them there. |
* |
* @author Thomas 'PointedEars' Lahn |
*/ |
abstract class Base |
{ |
/** |
* Determines if a strict model is enforced. |
* |
* If <code>true</code>, all publicly accessible properties |
* must have a getter if readable, and a setter if writable. |
* Otherwise, accesses to non-existing public properties will be |
* forwarded to the correspnding underline property if no getter |
* or setter has been defined for it. The default is |
* <code>false</code> (non-strict) as that speeds up property |
* accesses and eases implementation considerably. |
* |
* @var bool |
*/ |
protected static $_strict = false; |
/** |
* Retrieves a property value. |
* |
* Automagically called when attempting to read data |
* from inaccessible properties. |
* |
* @param string $name |
* Property name |
* @throws InvalidArgumentException |
* if the underscore property for the property |
* named <code><var>$name</var></code> does not exist |
* or has no getter |
* @return mixed |
* Return value of the property-specific getter |
*/ |
public function __get ($name) |
{ |
$getter = 'get' . ucfirst($name); |
if (method_exists($this, $getter)) |
{ |
return $this->$getter(); |
} |
if (property_exists($this, "_$name")) |
{ |
$class = get_class($this); |
if ($class::$_strict) |
{ |
throw new \InvalidArgumentException("Strict model: Property '{$name}' has no getter"); |
} |
return $this->{"_$name"}; |
} |
throw new \InvalidArgumentException("No such property: '{$name}'"); |
} |
/** |
* Sets a property value. |
* |
* Automagically called when attempting to write data |
* to inaccessible properties. |
* |
* @param string $name |
* Property name |
* @param mixed $value |
* Property value |
* @throws InvalidArgumentException |
* if the protected underscore property for the property |
* named <code><var>$name</var></code> does not exist |
* or has no setter (is read-only) |
* @return mixed |
* Return value of the property-specific setter |
*/ |
public function __set ($name, $value) |
{ |
$setter = 'set' . ucfirst($name); |
if (method_exists($this, $setter)) |
{ |
return $this->$setter($value); |
} |
if (property_exists($this, "_$name")) |
{ |
$class = get_class($this); |
if ($class::$_strict) |
{ |
throw new \InvalidArgumentException("Strict model: Property '{$name}' has no setter"); |
} |
$this->{"_$name"} = $value; |
return $value; |
} |
throw new \InvalidArgumentException("No such property: '{$name}'"); |
} |
} |
/trunk/View.php |
---|
0,0 → 1,358 |
<?php |
namespace PointedEars\PHPX; |
/** |
* A general view handled by a controller according to the MVC pattern |
* |
* @author Thomas 'PointedEars' Lahn <php@PointedEars.de> |
*/ |
class View |
{ |
/** |
* Default template resource path |
* |
* @var string |
*/ |
protected $_template = ''; |
/** |
* Content that can be inserted in the template |
* |
* @var string |
*/ |
protected $_content = ''; |
/** |
* Template variables. The variable name serves as item key, the item's value |
* is the variable value. |
* |
* @var array |
*/ |
protected $_template_vars = array(); |
/** |
* Stylesheets to be inserted into the <code>head</code> element |
* |
* @var array |
*/ |
protected $_stylesheets = array(); |
/** |
* Scripts to be inserted into the <code>head</code> or |
* <code>body</code> element |
* |
* @var array |
*/ |
protected $_scripts = array(); |
/** |
* Creates a new view |
* |
* @param string $template |
* Template resource path |
*/ |
public function __construct ($template) |
{ |
$this->_template = $template; |
} |
/** |
* Magic setter method used for defining template variables |
* |
* @param string $name |
* Variable name |
* @param mixed $value |
* Variable value |
*/ |
public function __set ($name, $value) |
{ |
$this->_template_vars[$name] = $value; |
} |
/** |
* Magic getter method used for retrieving values of template variables |
* |
* @param string $name |
* Variable name |
*/ |
public function __get ($name) |
{ |
return $this->_template_vars[$name]; |
} |
/** |
* Returns <var>$v</var> with occurences of '&' (ampersand), '"' (double quote), |
* "'" (single quote), '<' (less than), and '>' (greater than) replaced by their |
* HTML character entity references, if any, or their numeric HTML character |
* reference (as required primarily in HTML for attribute values and element |
* content). |
* |
* @param mixed $value |
*/ |
public function escape ($value) |
{ |
if (is_array($value)) |
{ |
return array_map(array('self', 'escape'), $value); |
} |
else if (is_object($value)) |
{ |
if ($value instanceof AbstractModel) |
{ |
foreach ($value->getPropertyVars() as $varName) |
{ |
$value->$varName = self::escape($value->$varName); |
} |
} |
return $value; |
} |
else |
{ |
if (is_string($value)) |
{ |
$encoding = mb_detect_encoding($value); |
if ($encoding === 'ASCII') |
{ |
$encoding = 'ISO-8859-1'; |
} |
return htmlspecialchars($value, ENT_QUOTES, $encoding); |
} |
return $value; |
} |
} |
/** |
* Assigns a value to a template variable |
* |
* @param string $name |
* Variable name |
* @param mixed $value |
* Variable value |
* @param bool $escape |
* If <code>true</code>, replace all potentially conflicting characters |
* in <var>$value</var> with their HTML entity references. The default is |
* <code>false</code>. |
* @return mixed The assigned value (after possible HTML encoding) |
* @see View::escape() |
*/ |
public function assign ($name, $value, $escape = false) |
{ |
if ($escape) |
{ |
$value = $this->escape($value); |
} |
$this->$name = $value; |
return $value; |
} |
/** |
* Adds an CSS resource (stylesheet) |
* to the list of external stylesheets |
* |
* @param string $uri |
* Stylesheet URI |
* @param mixed $key (optional) |
* Array key for the script. May be used later to exclude |
* or include the code for a specific stylesheet. |
* @return array |
* The list of stylesheets |
*/ |
public function addStylesheet ($uri, $key = null) |
{ |
$stylesheets =& $this->_stylesheets; |
if ($key !== null) |
{ |
$stylesheets[$key] = $uri; |
} |
else |
{ |
$stylesheets[] = $uri; |
} |
return $stylesheets; |
} |
/** |
* Adds several CSS resources (stylesheets) |
* to the list of external stylesheets |
* |
* @param array $uris |
* Stylesheet URIs |
* @return array |
* The list of stylesheets |
*/ |
public function addStylesheets (array $uris) |
{ |
$stylesheets = $this->_stylesheets; |
foreach ($uris as $uri) |
{ |
$stylesheets = $this->addStylesheet($uri); |
} |
return $stylesheets; |
} |
/** |
* Adds an ECMAScript resource (script) |
* to the list of external scripts |
* |
* @param string $uri |
* Script URI |
* @param mixed $key (optional) |
* Array key for the script. May be used later to exclude |
* or include the code for a specific script. |
*/ |
public function addScript ($uri, $key = null) |
{ |
$scripts =& $this->_scripts; |
if ($key !== null) |
{ |
$scripts[$key] = $uri; |
} |
else |
{ |
$scripts[] = $uri; |
} |
return $scripts; |
} |
/** |
* Adds several ECMAScript resources (scripts) |
* to the list of external scripts |
* |
* @param array $uris |
* Script URIs |
* @return array |
* The list of scripts |
*/ |
public function addScripts (array $uris) |
{ |
$scripts = $this->_scripts; |
foreach ($uris as $uri) |
{ |
$scripts = $this->addScript($uri); |
} |
return $scripts; |
} |
/** |
* Renders the view by including a template |
* |
* @param string $template |
* Optional alternative template resource path. |
* If not provided, the default template ($template property) will be used. |
* @throws Exception if no template has been defined before |
*/ |
public function render ($template = null, $content = null) |
{ |
if (!is_null($content)) |
{ |
ob_start(); |
require_once $content; |
$this->_content = ob_get_contents(); |
ob_end_clean(); |
} |
if (!is_null($template)) |
{ |
require $template; |
} |
elseif ($this->_template) |
{ |
require $this->_template; |
} |
else |
{ |
throw new \Exception('No template defined'); |
} |
} |
/** |
* Returns the code for including all stylesheets, |
* with exclusions. |
* |
* @param array $exclusions (optional, future) |
* The keys of the stylesheets that should be excluded |
* @return string |
*/ |
public function getStylesheets ($exclusions = array()) |
{ |
return ( |
implode(PHP_EOL, array_map(function ($uri) { |
return '<link rel="stylesheet" href="' . $this->escape($uri) . '">'; |
}, array_diff_key($this->_stylesheets, array_flip($exclusions)))) |
. PHP_EOL); |
} |
/** |
* Returns the code for including a specific stylesheet |
* |
* @param mixed $key |
* @return string |
*/ |
public function getStylesheet ($key) |
{ |
return '<link rel="stylesheet" href="' |
. $this->escape($this->_stylesheets[$key]) |
. '">' . PHP_EOL; |
} |
/** |
* Returns the code for including all stylesheets, |
* with exclusions. |
* |
* @param array $exclusions (optional, future) |
* The keys of the scripts that should be excluded. |
* Usually you would specify those scripts that you |
* want to include with {@link #getScript()}. |
* @return string |
*/ |
public function getScripts ($exclusions = array()) |
{ |
return ( |
implode(PHP_EOL, array_map(function ($uri) { |
return '<script type="text/javascript" src="' |
. $this->escape($uri) |
. '"></script>'; |
}, array_diff_key($this->_scripts, array_flip($exclusions)))) |
. PHP_EOL); |
} |
public function getScript ($key) |
{ |
return '<link rel="stylesheet" href="' |
. $this->escape($this->_scripts[$key]) |
. '">' . PHP_EOL; |
} |
/** |
* Returns the content for insertion into the template |
*/ |
public function getContent () |
{ |
return $this->_content; |
} |
/** |
* @param string[optional] $controller |
* @param string[optional] $action |
* @param int[optional] $id |
* @see Application::getURL() |
*/ |
public function getURL ($controller = null, $action = null, $id = null) |
{ |
return Application::getInstance()->getURL($controller, $action, $id); |
} |
} |
?> |
/trunk/Controller.php |
---|
0,0 → 1,138 |
<?php |
namespace PointedEars\PHPX; |
/** |
* A general controller that can handle views according to |
* the MVC pattern |
* |
* @author tlahn |
*/ |
abstract class Controller |
{ |
/** |
* Default action of the controller |
* @var string |
*/ |
protected $_defaultAction = 'index'; |
/** |
* If an array, maps an action name to an action method of the controller. |
* |
* Fallback for IE 7 where the content of a <code>button</code> element |
* is submitted as value. |
* |
* @var array |
*/ |
protected $_actionMap = null; |
/** |
* The {@link View} used by this controller |
* @var View |
*/ |
protected $_view = null; |
/** |
* Constructs a controller, initializes the related view, |
* and calls the controller's URI-indicated action method. |
* |
* @param string $viewClass |
* View class. The default is <code>'View'</code>. |
* @param string $template |
* Resource path of the template for the view. The default |
* is to <code>null</code> (no template). |
* |
* @see View::__construct() |
*/ |
protected function __construct ($viewClass = 'View', $template = null) |
{ |
$this->_view = new $viewClass($template); |
Application::getInstance()->setCurrentController($this); |
$action = Application::getParam('action', $_REQUEST); |
/* NOTE: No `==='; treat empty action like no specific action */ |
if ($action == null) |
{ |
$action = $this->_defaultAction; |
} |
$actionMethod = lcfirst($action) . 'Action'; |
/* Fallback for IE 7 where the content of a `button' element is submitted as value */ |
if (!method_exists($this, $actionMethod)) |
{ |
if (is_array($this->_actionMap) && array_key_exists($action, $this->_actionMap)) |
{ |
$actionMethod = $this->_actionMap[$action]; |
} |
} |
$this->$actionMethod(); |
} |
/** |
* (non-PHPDoc) |
* @see View::assign() |
*/ |
protected function assign ($name, $value, $encodeHTML = false) |
{ |
return $this->_view->assign($name, $value, $encodeHTML); |
} |
/** |
* (non-PHPDoc) |
* @see View::addStylesheet() |
*/ |
protected function addStylesheet ($uri, $key = null) |
{ |
return $this->_view->addStylesheet($uri, $key); |
} |
/** |
* (non-PHPDoc) |
* @see View::addStylesheets() |
*/ |
protected function addStylesheets (array $uris) |
{ |
return $this->_view->addStylesheets($uris); |
} |
/** |
* (non-PHPDoc) |
* @see View::addSscript() |
*/ |
protected function addScript ($uri, $key = null) |
{ |
return $this->_view->addScript($uri, $key); |
} |
/** |
* (non-PHPDoc) |
* @see View::addSscripts() |
*/ |
protected function addScripts (array $uris) |
{ |
return $this->_view->addScripts($uris); |
} |
/** |
* Renders the {@link View} associated with this controller |
* by including the <code>View</code>'s template. |
* <code>Controller</code>s should call this method instead of |
* <code>View::render()</code>. |
* |
* @param string $template |
* Optional alternative template resource path. |
* If not provided, the default template (the |
* <code>View</code>'s <code>$template</code> property) |
* will be used. |
*/ |
public function render ($template = null, $content = null) |
{ |
$this->_view->render($template, $content); |
} |
} |
?> |
/trunk/Model.php |
---|
0,0 → 1,308 |
<?php |
namespace PointedEars\PHPX; |
/** |
* Abstract model class for Object-Relational Mapping |
* |
* Provides simple mapping of a model object to records of |
* a table of a relational database. |
* |
* @property Db\Table $persistentTable |
* @author Thomas Lahn |
*/ |
abstract class Model extends \PointedEars\PHPX\AbstractModel |
{ |
/** |
* The <code>Table</code> for instances of this model |
* |
* @type Table|string |
*/ |
protected static $_persistentTable; |
/** |
* The name(s) of the property or properties whose value(s) |
* identify this object in the <code>Table</code>. They are |
* used for comparing against the primary key column(s) of |
* the <code>Table</code>. |
* |
* @type string|array[string] |
*/ |
protected static $_persistentId = 'id'; |
/** |
* The names of the properties that should be used in database |
* queries, and their mapping to the columns of |
* the <code>Table</code>, if specified (keys are property names, |
* values are column names, or both if the key is numeric). |
* |
* NOTE: It should not be necessary to include the |
* <code>persistentId</code> property value here. If an object |
* is not in the database, it should be assigned an ID |
* automatically when saved; if it is in the database, |
* you already have its ID as you searched by it. |
* |
* @type array |
*/ |
protected static $_persistentProperties = array(); |
/** |
* Creates a new model object |
* |
* @see AbstractModel::__construct() |
*/ |
public function __construct ( |
array $data = null, array $mapping = null, $exclusiveMapping = false) |
{ |
parent::__construct($data, $mapping, $exclusiveMapping); |
} |
public function getPersistentTable () |
{ |
$class = \get_class($this); |
if (\is_string($class::$_persistentTable)) |
{ |
/* Call setter to convert to Table */ |
$this->setPersistentTable($class::$_persistentTable); |
} |
return $class::$_persistentTable; |
} |
public function setPersistentTable ($value) |
{ |
$class = \get_class($this); |
if ($value instanceof Table) |
{ |
$class::$_persistentTable = $value; |
} |
else |
{ |
$table = new $value(); |
if (!($table instanceof Db\Table)) |
{ |
throw new \InvalidArgumentException( |
'Parameter does not specify a subclass of \\PointedEars\\PHPX\\Table: ' |
. $value |
); |
} |
$class::$_persistentTable = $table; |
} |
} |
/** |
* Returns an array containing the property-column mapping. |
* |
* @param array $propertyNames = null |
* Names of the properties that should be included. |
* The default is to include all persistent properties. |
* @return array |
*/ |
public static function getPersistentMapping (array $propertyNames = null) |
{ |
$a = array(); |
$persistent_properties = static::$_persistentProperties; |
if ($propertyNames === null) |
{ |
$propertyNames = $persistent_properties; |
} |
foreach ($propertyNames as $property_name) |
{ |
if (!array_key_exists($property_name, $persistent_properties)) |
{ |
$column_name = $property_name; |
} |
else |
{ |
$column_name = $persistent_properties[$property_name]; |
} |
$a[$property_name] = $column_name; |
} |
return $a; |
} |
/** |
* Returns an array for database queries containing the |
* property values of this object, using the specified |
* property-to-column mapping. |
* |
* @param array $propertyNames = null |
* Names of the properties that should be included. |
* The default is to include all persistent properties. |
* @return array |
*/ |
public function getPropertyArray (array $propertyNames = null) |
{ |
$a = array(); |
if ($propertyNames === null) |
{ |
$class = \get_class($this); |
$propertyNames = $class::$_persistentProperties; |
} |
foreach ($propertyNames as $propertyName => $columnName) |
{ |
if (is_numeric($propertyName)) |
{ |
$propertyName = $columnName; |
} |
$a[$columnName] = $this->$propertyName; |
} |
return $a; |
} |
/** |
* Finds the record for the model object in the table, fills |
* the object with missing data, and returns the result. |
* |
* @param mixed $id = null |
* The ID of the object to find. If <code>null</code> (default), |
* the object is searched for by its current ID. |
* @return Model|null |
* This object filled with missing data, or <code>null</code> |
* if there is no data for this object |
* @see Table::find(int) |
*/ |
public function find ($id = null) |
{ |
$class = \get_class($this); |
if ($id === null) |
{ |
$id = $this->{$class::$_persistentId}; |
} |
$result = $this->persistentTable->find($id); |
if ($id !== null) |
{ |
$this->{$class::$_persistentId} = $id; |
} |
$result = $this->persistentTable->find($this->{$class::$_persistentId}); |
if ($result) |
{ |
return $this->map($result); |
} |
return null; |
} |
/** |
* Finds the records for model objects in the table by value, |
* and returns the result. |
* |
* @param string $name |
* Name of the property to search for. |
* @param mixed $value = null |
* The property value to find. If <code>null</code>, |
* the current value of the specified property is used. |
* (To search for null, you need to make sure that the |
* current property value is <code>null</code>, which is |
* the PHP default.) |
* @return array[Model]|null |
* Model objects, or <code>null</code> if there is no data |
* for this property and value |
* @see Table::select(Model) |
*/ |
public function findByProperty ($name, $value = null) |
{ |
$class = \get_class($this); |
$mapping = $class::getPersistentMapping(array($name)); |
if ($value === null) |
{ |
$value = $this->$name; |
} |
$results = $this->persistentTable->select(null, |
array($mapping[$name] => $value)); |
if ($results) |
{ |
return array_map(array($this, 'map'), $results); |
} |
return null; |
} |
/** |
* Saves the model object in the table |
* |
* @param array $propertyNames = null |
* Names of the properties whose values should be saved |
* in the database. The default is to save the values |
* of all persistent properties. |
* @return boolean |
* The return value of |
* <code>$this->persistentTable->updateOrInsert()</code>. |
* @see Model::getPropertyArray() |
* @see Table::updateOrInsert() |
*/ |
public function save (array $propertyNames = null) |
{ |
$table = $this->persistentTable; |
$class = \get_class($this); |
$idPropertyName = $class::$_persistentId; |
$result = $table->updateOrInsert( |
$this->getPropertyArray($propertyNames), |
array( |
$table->id => $this->$idPropertyName |
) |
); |
if ($result && ($lastInsertId = $table->lastInsertId)) |
{ |
$this->$idPropertyName = $lastInsertId; |
} |
return $result; |
} |
/** |
* Inserts the model object into the table |
* |
* @param array $propertyNames = null |
* Names of the properties whose values should be insert |
* in the database. The default is to insert the values |
* of all persistent properties. |
* @return boolean |
* @see Model::getPropertyArray() |
* @see Table::insert() |
*/ |
public function insert (array $propertyNames = null) |
{ |
$table = $this->persistentTable; |
$class = \get_class($this); |
$idPropertyName = $class::$_persistentId; |
$result = $table->insert($this->getPropertyArray($propertyNames)); |
if ($result && ($lastInsertId = $table->lastInsertId)) |
{ |
$this->$idPropertyName = $lastInsertId; |
} |
return $result; |
} |
/** |
* Deletes a model object from the <code>Table</code> |
* |
* @return bool |
* @see Table::delete() |
*/ |
public function delete () |
{ |
$class = \get_class($this); |
return $this->persistentTable->delete($this->{$class::$_persistentId}); |
} |
} |
/trunk/AbstractModel.php |
---|
0,0 → 1,140 |
<?php |
namespace PointedEars\PHPX; |
/** |
* Interface to be implemented if the model should be localizable |
*/ |
interface ILocalizable |
{ |
/** |
* Localizes this model. The actual implementation is left to |
* the model class implementing this interface. |
*/ |
function localize (); |
} |
/** |
* Abstract model class |
* |
* Provides a constructor to initialize properties using setters and getters. |
* |
* @author Thomas Lahn |
*/ |
abstract class AbstractModel extends Base |
{ |
/** |
* Creates a new model object |
* |
* @param array $data |
* Initialization data (optional) |
* @param array $mapping |
* Mapping for initialization data (optional) |
* @param bool $exclusiveMapping |
* Exclusive mapping (optional; default: inclusive) |
* @see AbstractModel::map() |
*/ |
protected function __construct ( |
array $data = null, array $mapping = null, $exclusiveMapping = false) |
{ |
if (!is_null($data)) |
{ |
$this->map($data, $mapping, $exclusiveMapping); |
} |
} |
/** |
* Returns <code>true</code> if a variable name is a property variable name |
* (starts with <tt>$_</tt>), <code>false</code> otherwise. |
* |
* @param string $varName |
* @return boolean |
* @see getPropertyVars() |
*/ |
private static function _isPropertyVar ($varName) |
{ |
return preg_match('/^_\\w/', $varName) > 0; |
} |
/** |
* Returns <code>true</code> if a variable name is a property variable name |
* (starts with <tt>$_</tt>), <code>false</code> otherwise. |
* |
* @param string $varName |
* @return string |
* @see getPropertyVars() |
*/ |
private static function _toPropertyVar ($varName) |
{ |
return preg_replace('/^_(\\w)/', '\\1', $varName); |
} |
/** |
* Returns the public names of the property variables of a {@link Model} |
* as an array of strings |
* |
* @return array |
*/ |
public function getPropertyVars () |
{ |
return array_map( |
array('self', '_toPropertyVar'), |
array_filter( |
array_keys(get_object_vars($this)), |
array('self', '_isPropertyVar') |
) |
); |
} |
/** |
* Maps the values of an associative array to a model object |
* |
* @param array $data |
* Data to be mapped to properties of the object |
* @param array $mapping = null |
* <p>If <var>$mapping</var> is not provided, or <code>null</code> (default), |
* the values of <var>$data</var> are mapped to properties of |
* the model object as specified by the keys of <var>$data</var>.</p> |
* <p>If <var>$mapping</var> is provided and an array, the keys of |
* <var>$data</var> are mapped to properties as specified by |
* the corresponding values of <var>$mapping</var>. If a value of |
* <var>$mapping</var> is <code>null</code>, the corresponding value |
* in <var>$data</var> is not mapped; if a key is missing in |
* <var>$mapping</var>, the value is mapped as if <var>$mapping</var> |
* was <code>null</code>.</p> |
* @param bool $exclusive = false |
* <p>If <code>true</code>, <em>only</em> the keys of <var>$data</var> |
* that are present in <var>$mapping</var> are mapped.</p> |
* @return AbstractModel |
* The modified object |
*/ |
public function map (array $data, array $mapping = null, $exclusive = false) |
{ |
if (is_null($mapping)) |
{ |
foreach ($data as $key => $value) |
{ |
$this->$key = $value; |
} |
} |
else |
{ |
foreach ($data as $key => $value) |
{ |
if (array_key_exists($key, $mapping)) |
{ |
if ($exclusive || !is_null($mapping[$key])) |
{ |
$this->{$mapping[$key]} = $value; |
} |
} |
else |
{ |
$this->$key = $value; |
} |
} |
} |
return $this; |
} |
} |
/trunk/Registry.php |
---|
0,0 → 1,41 |
<?php |
namespace PointedEars\PHPX; |
/** |
* Basic registry class |
* |
* @author Thomas Lahn |
*/ |
abstract class Registry |
{ |
/** |
* Data storage |
* @var array |
*/ |
protected static $_data = array(); |
/** |
* Puts data in storage |
* |
* @param string $key |
* @param mixed $value |
* @return mixed The stored value |
*/ |
public static function set($key, $value) |
{ |
self::$_data[$key] = $value; |
return self::get($key); |
} |
/** |
* Gets data from storage |
* |
* @param string $key |
* @return mixed |
*/ |
public static function get($key) |
{ |
return self::$_data[$key]; |
} |
} |
/trunk/dom/tests/DOMDocument2Test.php |
---|
0,0 → 1,48 |
<?php |
require_once __DIR__ . '/../DOMDocument2.php'; |
use de\pointedears\dom\DOMDocument2; |
class DOMDocument2Test extends \PHPUnit_Framework_Testcase |
{ |
public function testRenameElementNode () |
{ |
$d = new DOMDocument2(); |
$d->appendChild($d->createElement("foo")) |
->appendChild($d->createElement("bar")) |
->setAttributeNS(null, 'evil', 23); |
$d->firstChild->setAttributeNS(null, 'answer', 42); |
echo "\nBefore:\n\n" . $d->saveXML() . "\n"; |
$d->renameNode($d->firstChild, null, "baz"); |
echo "After:\n\n" . $d->saveXML() . "\n"; |
$this->assertTrue($d->firstChild->nodeName === 'baz'); |
$this->assertTrue($d->firstChild->firstChild->nodeName === 'bar'); |
} |
public function testRenameAttributeNode () |
{ |
$d = new DOMDocument2(); |
$d->appendChild($d->createElement("foo")) |
->setAttributeNS(null, 'evil', 42); |
echo "\nBefore:\n\n" . $d->saveXML() . "\n"; |
$attrNode = $d->firstChild->getAttributeNode("evil"); |
$d->renameNode($attrNode, null, "answer"); |
echo "After:\n\n" . $d->saveXML(); |
$this->assertTrue($d->firstChild->getAttribute("answer") === "42"); |
} |
public function testRenameStandaloneAttr () |
{ |
$d = new DOMDocument2(); |
$attr = $d->renameNode(new \DOMAttr('evil', '42'), null, "answer"); |
$this->assertTrue($attr->name === "answer" && $attr->value === "42"); |
} |
} |
/trunk/dom/DOMDocument2.php |
---|
0,0 → 1,93 |
<?php |
namespace de\pointedears\dom; |
class DOMDocument2 extends \DOMDocument |
{ |
/** |
* Renames an existing node of type <code>XML_ELEMENT_NODE</code> |
* or <code>XML_ATTRIBUTE_NODE</code> |
* |
* @param \DOMNode $node |
* @param string $namespaceURI = null |
* @param string $qualifiedName |
* @return \DOMNode The renamed node |
*/ |
public function renameNode (\DOMNode $node, $namespaceURI, |
$qualifiedName) |
{ |
$result = false; |
if ($node->nodeType === XML_ELEMENT_NODE) |
{ |
/* |
* Per W3C DOM Level 3 Core |
* <http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-renameNode> |
* |
* "If simply changing the name of the given node is not possible, |
* the following operations are performed: |
* |
* a new node is created, ... |
*/ |
$newNode = $this->createElementNS($namespaceURI, |
$qualifiedName, $node->nodeValue); |
/* |
* any registered event listener is registered on the new node |
* [skipped], any user data attached to the old node is removed |
* from that node the old node is removed from its parent if it |
* has one [done by replaceChild] |
*/ |
/* the children are moved to the new node */ |
while (($childNode = $node->firstChild)) |
{ |
$newNode->appendChild($childNode); |
} |
/* |
* if the renamed node is an Element its attributes are moved |
* to the new node, |
*/ |
foreach ($node->attributes as $attribute) |
{ |
$newNode->setAttributeNodeNS($attribute); |
} |
/* |
* the new node is inserted at the position the old node used |
* to have in its parent's child nodes list if it has one, |
* the user data that was attached to the old node is attached |
* to the new node." |
*/ |
$result = $node->parentNode->replaceChild($newNode, $node); |
} |
else if ($node->nodeType === XML_ATTRIBUTE_NODE) |
{ |
/* |
* "When the node being renamed is an Attr that is attached |
* to an Element, the node is first removed from the Element |
* attributes map. Then, once renamed, either by modifying |
* the existing node or creating a new one as described above, |
* it is put back." |
*/ |
if (($ownerElement = $node->ownerElement)) |
{ |
$ownerElement->removeAttributeNS($node->namespaceURI, $node->name); |
$result = $ownerElement->setAttributeNS($namespaceURI, $qualifiedName, $node->value); |
if ($result !== false) |
{ |
$newNode = $ownerElement->getAttributeNode($qualifiedName); |
} |
} |
else |
{ |
/* Standalone attribute node */ |
return new \DOMAttr($qualifiedName, $node->value); |
} |
} |
return ($result === false ? false : $newNode); |
} |
} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/SplAutoloader.php |
---|
0,0 → 1,63 |
<?php |
/** |
* Zend Framework (http://framework.zend.com/) |
* |
* @link http://github.com/zendframework/zf2 for the canonical source repository |
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
* @license http://framework.zend.com/license/new-bsd New BSD License |
*/ |
namespace Zend\Loader; |
use Traversable; |
if (interface_exists('Zend\Loader\SplAutoloader')) return; |
/** |
* Defines an interface for classes that may register with the spl_autoload |
* registry |
*/ |
interface SplAutoloader |
{ |
/** |
* Constructor |
* |
* Allow configuration of the autoloader via the constructor. |
* |
* @param null|array|Traversable $options |
*/ |
public function __construct($options = null); |
/** |
* Configure the autoloader |
* |
* In most cases, $options should be either an associative array or |
* Traversable object. |
* |
* @param array|Traversable $options |
* @return SplAutoloader |
*/ |
public function setOptions($options); |
/** |
* Autoload a class |
* |
* @param $class |
* @return mixed |
* False [if unable to load $class] |
* get_class($class) [if $class is successfully loaded] |
*/ |
public function autoload($class); |
/** |
* Register the autoloader with spl_autoload registry |
* |
* Typically, the body of this will simply be: |
* <code> |
* spl_autoload_register(array($this, 'autoload')); |
* </code> |
* |
* @return void |
*/ |
public function register(); |
} |
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/Loader/StandardAutoloader.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Loader/StandardAutoloader.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Loader/StandardAutoloader.php (revision 80) |
@@ -0,0 +1,327 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Loader; |
+ |
+// Grab SplAutoloader interface |
+require_once __DIR__ . '/SplAutoloader.php'; |
+ |
+/** |
+ * PSR-0 compliant autoloader |
+ * |
+ * Allows autoloading both namespaced and vendor-prefixed classes. Class |
+ * lookups are performed on the filesystem. If a class file for the referenced |
+ * class is not found, a PHP warning will be raised by include(). |
+ */ |
+class StandardAutoloader implements SplAutoloader |
+{ |
+ const NS_SEPARATOR = '\\'; |
+ const PREFIX_SEPARATOR = '_'; |
+ const LOAD_NS = 'namespaces'; |
+ const LOAD_PREFIX = 'prefixes'; |
+ const ACT_AS_FALLBACK = 'fallback_autoloader'; |
+ const AUTOREGISTER_ZF = 'autoregister_zf'; |
+ |
+ /** |
+ * @var array Namespace/directory pairs to search; ZF library added by default |
+ */ |
+ protected $namespaces = array(); |
+ |
+ /** |
+ * @var array Prefix/directory pairs to search |
+ */ |
+ protected $prefixes = array(); |
+ |
+ /** |
+ * @var bool Whether or not the autoloader should also act as a fallback autoloader |
+ */ |
+ protected $fallbackAutoloaderFlag = false; |
+ |
+ /** |
+ * Constructor |
+ * |
+ * @param null|array|\Traversable $options |
+ */ |
+ public function __construct($options = null) |
+ { |
+ if (null !== $options) { |
+ $this->setOptions($options); |
+ } |
+ } |
+ |
+ /** |
+ * Configure autoloader |
+ * |
+ * Allows specifying both "namespace" and "prefix" pairs, using the |
+ * following structure: |
+ * <code> |
+ * array( |
+ * 'namespaces' => array( |
+ * 'Zend' => '/path/to/Zend/library', |
+ * 'Doctrine' => '/path/to/Doctrine/library', |
+ * ), |
+ * 'prefixes' => array( |
+ * 'Phly_' => '/path/to/Phly/library', |
+ * ), |
+ * 'fallback_autoloader' => true, |
+ * ) |
+ * </code> |
+ * |
+ * @param array|\Traversable $options |
+ * @throws Exception\InvalidArgumentException |
+ * @return StandardAutoloader |
+ */ |
+ public function setOptions($options) |
+ { |
+ if (!is_array($options) && !($options instanceof \Traversable)) { |
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php'; |
+ throw new Exception\InvalidArgumentException('Options must be either an array or Traversable'); |
+ } |
+ |
+ foreach ($options as $type => $pairs) { |
+ switch ($type) { |
+ case self::AUTOREGISTER_ZF: |
+ if ($pairs) { |
+ $this->registerNamespace('Zend', dirname(__DIR__)); |
+ } |
+ break; |
+ case self::LOAD_NS: |
+ if (is_array($pairs) || $pairs instanceof \Traversable) { |
+ $this->registerNamespaces($pairs); |
+ } |
+ break; |
+ case self::LOAD_PREFIX: |
+ if (is_array($pairs) || $pairs instanceof \Traversable) { |
+ $this->registerPrefixes($pairs); |
+ } |
+ break; |
+ case self::ACT_AS_FALLBACK: |
+ $this->setFallbackAutoloader($pairs); |
+ break; |
+ default: |
+ // ignore |
+ } |
+ } |
+ return $this; |
+ } |
+ |
+ /** |
+ * Set flag indicating fallback autoloader status |
+ * |
+ * @param bool $flag |
+ * @return StandardAutoloader |
+ */ |
+ public function setFallbackAutoloader($flag) |
+ { |
+ $this->fallbackAutoloaderFlag = (bool) $flag; |
+ return $this; |
+ } |
+ |
+ /** |
+ * Is this autoloader acting as a fallback autoloader? |
+ * |
+ * @return bool |
+ */ |
+ public function isFallbackAutoloader() |
+ { |
+ return $this->fallbackAutoloaderFlag; |
+ } |
+ |
+ /** |
+ * Register a namespace/directory pair |
+ * |
+ * @param string $namespace |
+ * @param string $directory |
+ * @return StandardAutoloader |
+ */ |
+ public function registerNamespace($namespace, $directory) |
+ { |
+ $namespace = rtrim($namespace, self::NS_SEPARATOR) . self::NS_SEPARATOR; |
+ $this->namespaces[$namespace] = $this->normalizeDirectory($directory); |
+ return $this; |
+ } |
+ |
+ /** |
+ * Register many namespace/directory pairs at once |
+ * |
+ * @param array $namespaces |
+ * @throws Exception\InvalidArgumentException |
+ * @return StandardAutoloader |
+ */ |
+ public function registerNamespaces($namespaces) |
+ { |
+ if (!is_array($namespaces) && !$namespaces instanceof \Traversable) { |
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php'; |
+ throw new Exception\InvalidArgumentException('Namespace pairs must be either an array or Traversable'); |
+ } |
+ |
+ foreach ($namespaces as $namespace => $directory) { |
+ $this->registerNamespace($namespace, $directory); |
+ } |
+ return $this; |
+ } |
+ |
+ /** |
+ * Register a prefix/directory pair |
+ * |
+ * @param string $prefix |
+ * @param string $directory |
+ * @return StandardAutoloader |
+ */ |
+ public function registerPrefix($prefix, $directory) |
+ { |
+ $prefix = rtrim($prefix, self::PREFIX_SEPARATOR). self::PREFIX_SEPARATOR; |
+ $this->prefixes[$prefix] = $this->normalizeDirectory($directory); |
+ return $this; |
+ } |
+ |
+ /** |
+ * Register many namespace/directory pairs at once |
+ * |
+ * @param array $prefixes |
+ * @throws Exception\InvalidArgumentException |
+ * @return StandardAutoloader |
+ */ |
+ public function registerPrefixes($prefixes) |
+ { |
+ if (!is_array($prefixes) && !$prefixes instanceof \Traversable) { |
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php'; |
+ throw new Exception\InvalidArgumentException('Prefix pairs must be either an array or Traversable'); |
+ } |
+ |
+ foreach ($prefixes as $prefix => $directory) { |
+ $this->registerPrefix($prefix, $directory); |
+ } |
+ return $this; |
+ } |
+ |
+ /** |
+ * Defined by Autoloadable; autoload a class |
+ * |
+ * @param string $class |
+ * @return false|string |
+ */ |
+ public function autoload($class) |
+ { |
+ $isFallback = $this->isFallbackAutoloader(); |
+ if (false !== strpos($class, self::NS_SEPARATOR)) { |
+ if ($this->loadClass($class, self::LOAD_NS)) { |
+ return $class; |
+ } elseif ($isFallback) { |
+ return $this->loadClass($class, self::ACT_AS_FALLBACK); |
+ } |
+ return false; |
+ } |
+ if (false !== strpos($class, self::PREFIX_SEPARATOR)) { |
+ if ($this->loadClass($class, self::LOAD_PREFIX)) { |
+ return $class; |
+ } elseif ($isFallback) { |
+ return $this->loadClass($class, self::ACT_AS_FALLBACK); |
+ } |
+ return false; |
+ } |
+ if ($isFallback) { |
+ return $this->loadClass($class, self::ACT_AS_FALLBACK); |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Register the autoloader with spl_autoload |
+ * |
+ * @return void |
+ */ |
+ public function register() |
+ { |
+ spl_autoload_register(array($this, 'autoload')); |
+ } |
+ |
+ /** |
+ * Transform the class name to a filename |
+ * |
+ * @param string $class |
+ * @param string $directory |
+ * @return string |
+ */ |
+ protected function transformClassNameToFilename($class, $directory) |
+ { |
+ // $class may contain a namespace portion, in which case we need |
+ // to preserve any underscores in that portion. |
+ $matches = array(); |
+ preg_match('/(?P<namespace>.+\\\)?(?P<class>[^\\\]+$)/', $class, $matches); |
+ |
+ $class = (isset($matches['class'])) ? $matches['class'] : ''; |
+ $namespace = (isset($matches['namespace'])) ? $matches['namespace'] : ''; |
+ |
+ return $directory |
+ . str_replace(self::NS_SEPARATOR, '/', $namespace) |
+ . str_replace(self::PREFIX_SEPARATOR, '/', $class) |
+ . '.php'; |
+ } |
+ |
+ /** |
+ * Load a class, based on its type (namespaced or prefixed) |
+ * |
+ * @param string $class |
+ * @param string $type |
+ * @return bool|string |
+ * @throws Exception\InvalidArgumentException |
+ */ |
+ protected function loadClass($class, $type) |
+ { |
+ if (!in_array($type, array(self::LOAD_NS, self::LOAD_PREFIX, self::ACT_AS_FALLBACK))) { |
+ require_once __DIR__ . '/Exception/InvalidArgumentException.php'; |
+ throw new Exception\InvalidArgumentException(); |
+ } |
+ |
+ // Fallback autoloading |
+ if ($type === self::ACT_AS_FALLBACK) { |
+ // create filename |
+ $filename = $this->transformClassNameToFilename($class, ''); |
+ $resolvedName = stream_resolve_include_path($filename); |
+ if ($resolvedName !== false) { |
+ return include $resolvedName; |
+ } |
+ return false; |
+ } |
+ |
+ // Namespace and/or prefix autoloading |
+ foreach ($this->$type as $leader => $path) { |
+ if (0 === strpos($class, $leader)) { |
+ // Trim off leader (namespace or prefix) |
+ $trimmedClass = substr($class, strlen($leader)); |
+ |
+ // create filename |
+ $filename = $this->transformClassNameToFilename($trimmedClass, $path); |
+ if (file_exists($filename)) { |
+ return include $filename; |
+ } |
+ return false; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Normalize the directory to include a trailing directory separator |
+ * |
+ * @param string $directory |
+ * @return string |
+ */ |
+ protected function normalizeDirectory($directory) |
+ { |
+ $last = $directory[strlen($directory) - 1]; |
+ if (in_array($last, array('/', '\\'))) { |
+ $directory[strlen($directory) - 1] = DIRECTORY_SEPARATOR; |
+ return $directory; |
+ } |
+ $directory .= DIRECTORY_SEPARATOR; |
+ return $directory; |
+ } |
+} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/StandardAutoloader.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/MissingResourceNamespaceException.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/MissingResourceNamespaceException.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/MissingResourceNamespaceException.php (revision 80) |
@@ -0,0 +1,16 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Loader\Exception; |
+ |
+require_once __DIR__ . '/ExceptionInterface.php'; |
+ |
+class MissingResourceNamespaceException extends \Exception implements |
+ ExceptionInterface |
+{} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/MissingResourceNamespaceException.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/ExceptionInterface.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/ExceptionInterface.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/ExceptionInterface.php (revision 80) |
@@ -0,0 +1,13 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Loader\Exception; |
+ |
+interface ExceptionInterface |
+{} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/ExceptionInterface.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/BadMethodCallException.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/BadMethodCallException.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/BadMethodCallException.php (revision 80) |
@@ -0,0 +1,17 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Loader\Exception; |
+ |
+require_once __DIR__ . '/ExceptionInterface.php'; |
+ |
+class BadMethodCallException extends \BadMethodCallException implements |
+ ExceptionInterface |
+{ |
+} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/BadMethodCallException.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/InvalidPathException.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/InvalidPathException.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/InvalidPathException.php (revision 80) |
@@ -0,0 +1,15 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Loader\Exception; |
+ |
+require_once __DIR__ . '/ExceptionInterface.php'; |
+ |
+class InvalidPathException extends \Exception implements ExceptionInterface |
+{} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/InvalidPathException.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/RuntimeException.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/RuntimeException.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/RuntimeException.php (revision 80) |
@@ -0,0 +1,15 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Loader\Exception; |
+ |
+require_once __DIR__ . '/ExceptionInterface.php'; |
+ |
+class RuntimeException extends \RuntimeException implements ExceptionInterface |
+{} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/RuntimeException.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/PluginLoaderException.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/PluginLoaderException.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/PluginLoaderException.php (revision 80) |
@@ -0,0 +1,19 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Loader\Exception; |
+ |
+require_once __DIR__ . '/DomainException.php'; |
+ |
+/** |
+ * Plugin class loader exceptions |
+ */ |
+class PluginLoaderException extends DomainException |
+{ |
+} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/PluginLoaderException.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/DomainException.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/DomainException.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/DomainException.php (revision 80) |
@@ -0,0 +1,15 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Loader\Exception; |
+ |
+require_once __DIR__ . '/ExceptionInterface.php'; |
+ |
+class DomainException extends \DomainException implements ExceptionInterface |
+{} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/DomainException.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/SecurityException.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/SecurityException.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/SecurityException.php (revision 80) |
@@ -0,0 +1,15 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Loader\Exception; |
+ |
+require_once __DIR__ . '/DomainException.php'; |
+ |
+class SecurityException extends DomainException |
+{} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/SecurityException.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/InvalidArgumentException.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/InvalidArgumentException.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/InvalidArgumentException.php (revision 80) |
@@ -0,0 +1,16 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Loader\Exception; |
+ |
+require_once __DIR__ . '/ExceptionInterface.php'; |
+ |
+class InvalidArgumentException extends \InvalidArgumentException implements |
+ ExceptionInterface |
+{} |
/trunk/ZendFramework-2.1.2/library/Zend/Loader/Exception/InvalidArgumentException.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Translator.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Translator.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Translator.php (revision 80) |
@@ -0,0 +1,586 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\I18n\Translator; |
+ |
+use Locale; |
+use Traversable; |
+use Zend\Cache; |
+use Zend\Cache\Storage\StorageInterface as CacheStorage; |
+use Zend\I18n\Exception; |
+use Zend\I18n\Translator\Loader\FileLoaderInterface; |
+use Zend\I18n\Translator\Loader\RemoteLoaderInterface; |
+use Zend\Stdlib\ArrayUtils; |
+ |
+/** |
+ * Translator. |
+ */ |
+class Translator |
+{ |
+ /** |
+ * Messages loaded by the translator. |
+ * |
+ * @var array |
+ */ |
+ protected $messages = array(); |
+ |
+ /** |
+ * Files used for loading messages. |
+ * |
+ * @var array |
+ */ |
+ protected $files = array(); |
+ |
+ /** |
+ * Patterns used for loading messages. |
+ * |
+ * @var array |
+ */ |
+ protected $patterns = array(); |
+ |
+ /** |
+ * Remote locations for loading messages. |
+ * |
+ * @var array |
+ */ |
+ protected $remote = array(); |
+ |
+ /** |
+ * Default locale. |
+ * |
+ * @var string |
+ */ |
+ protected $locale; |
+ |
+ /** |
+ * Locale to use as fallback if there is no translation. |
+ * |
+ * @var string |
+ */ |
+ protected $fallbackLocale; |
+ |
+ /** |
+ * Translation cache. |
+ * |
+ * @var CacheStorage |
+ */ |
+ protected $cache; |
+ |
+ /** |
+ * Plugin manager for translation loaders. |
+ * |
+ * @var LoaderPluginManager |
+ */ |
+ protected $pluginManager; |
+ |
+ /** |
+ * Instantiate a translator |
+ * |
+ * @param array|Traversable $options |
+ * @return Translator |
+ * @throws Exception\InvalidArgumentException |
+ */ |
+ public static function factory($options) |
+ { |
+ if ($options instanceof Traversable) { |
+ $options = ArrayUtils::iteratorToArray($options); |
+ } elseif (!is_array($options)) { |
+ throw new Exception\InvalidArgumentException(sprintf( |
+ '%s expects an array or Traversable object; received "%s"', |
+ __METHOD__, |
+ (is_object($options) ? get_class($options) : gettype($options)) |
+ )); |
+ } |
+ |
+ $translator = new static(); |
+ |
+ // locales |
+ if (isset($options['locale'])) { |
+ $locales = (array) $options['locale']; |
+ $translator->setLocale(array_shift($locales)); |
+ if (count($locales) > 0) { |
+ $translator->setFallbackLocale(array_shift($locales)); |
+ } |
+ } |
+ |
+ // file patterns |
+ if (isset($options['translation_file_patterns'])) { |
+ if (!is_array($options['translation_file_patterns'])) { |
+ throw new Exception\InvalidArgumentException( |
+ '"translation_file_patterns" should be an array' |
+ ); |
+ } |
+ |
+ $requiredKeys = array('type', 'base_dir', 'pattern'); |
+ foreach ($options['translation_file_patterns'] as $pattern) { |
+ foreach ($requiredKeys as $key) { |
+ if (!isset($pattern[$key])) { |
+ throw new Exception\InvalidArgumentException( |
+ "'{$key}' is missing for translation pattern options" |
+ ); |
+ } |
+ } |
+ |
+ $translator->addTranslationFilePattern( |
+ $pattern['type'], |
+ $pattern['base_dir'], |
+ $pattern['pattern'], |
+ isset($pattern['text_domain']) ? $pattern['text_domain'] : 'default' |
+ ); |
+ } |
+ } |
+ |
+ // files |
+ if (isset($options['translation_files'])) { |
+ if (!is_array($options['translation_files'])) { |
+ throw new Exception\InvalidArgumentException( |
+ '"translation_files" should be an array' |
+ ); |
+ } |
+ |
+ $requiredKeys = array('type', 'filename'); |
+ foreach ($options['translation_files'] as $file) { |
+ foreach ($requiredKeys as $key) { |
+ if (!isset($file[$key])) { |
+ throw new Exception\InvalidArgumentException( |
+ "'{$key}' is missing for translation file options" |
+ ); |
+ } |
+ } |
+ |
+ $translator->addTranslationFile( |
+ $file['type'], |
+ $file['filename'], |
+ isset($file['text_domain']) ? $file['text_domain'] : 'default', |
+ isset($file['locale']) ? $file['locale'] : null |
+ ); |
+ } |
+ } |
+ |
+ // remote |
+ if (isset($options['remote_translation'])) { |
+ if (!is_array($options['remote_translation'])) { |
+ throw new Exception\InvalidArgumentException( |
+ '"remote_translation" should be an array' |
+ ); |
+ } |
+ |
+ $requiredKeys = array('type'); |
+ foreach ($options['remote_translation'] as $remote) { |
+ foreach ($requiredKeys as $key) { |
+ if (!isset($remote[$key])) { |
+ throw new Exception\InvalidArgumentException( |
+ "'{$key}' is missing for remote translation options" |
+ ); |
+ } |
+ } |
+ |
+ $translator->addRemoteTranslations( |
+ $remote['type'], |
+ isset($remote['text_domain']) ? $remote['text_domain'] : 'default' |
+ ); |
+ } |
+ } |
+ |
+ // cache |
+ if (isset($options['cache'])) { |
+ if ($options['cache'] instanceof CacheStorage) { |
+ $translator->setCache($options['cache']); |
+ } else { |
+ $translator->setCache(Cache\StorageFactory::factory($options['cache'])); |
+ } |
+ } |
+ |
+ return $translator; |
+ } |
+ |
+ /** |
+ * Set the default locale. |
+ * |
+ * @param string $locale |
+ * @return Translator |
+ */ |
+ public function setLocale($locale) |
+ { |
+ $this->locale = $locale; |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * Get the default locale. |
+ * |
+ * @return string |
+ */ |
+ public function getLocale() |
+ { |
+ if ($this->locale === null) { |
+ $this->locale = Locale::getDefault(); |
+ } |
+ |
+ return $this->locale; |
+ } |
+ |
+ /** |
+ * Set the fallback locale. |
+ * |
+ * @param string $locale |
+ * @return Translator |
+ */ |
+ public function setFallbackLocale($locale) |
+ { |
+ $this->fallbackLocale = $locale; |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * Get the fallback locale. |
+ * |
+ * @return string |
+ */ |
+ public function getFallbackLocale() |
+ { |
+ return $this->fallbackLocale; |
+ } |
+ |
+ /** |
+ * Sets a cache |
+ * |
+ * @param CacheStorage $cache |
+ * @return Translator |
+ */ |
+ public function setCache(CacheStorage $cache = null) |
+ { |
+ $this->cache = $cache; |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * Returns the set cache |
+ * |
+ * @return CacheStorage The set cache |
+ */ |
+ public function getCache() |
+ { |
+ return $this->cache; |
+ } |
+ |
+ /** |
+ * Set the plugin manager for translation loaders |
+ * |
+ * @param LoaderPluginManager $pluginManager |
+ * @return Translator |
+ */ |
+ public function setPluginManager(LoaderPluginManager $pluginManager) |
+ { |
+ $this->pluginManager = $pluginManager; |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * Retrieve the plugin manager for translation loaders. |
+ * |
+ * Lazy loads an instance if none currently set. |
+ * |
+ * @return LoaderPluginManager |
+ */ |
+ public function getPluginManager() |
+ { |
+ if (!$this->pluginManager instanceof LoaderPluginManager) { |
+ $this->setPluginManager(new LoaderPluginManager()); |
+ } |
+ |
+ return $this->pluginManager; |
+ } |
+ |
+ /** |
+ * Translate a message. |
+ * |
+ * @param string $message |
+ * @param string $textDomain |
+ * @param string $locale |
+ * @return string |
+ */ |
+ public function translate($message, $textDomain = 'default', $locale = null) |
+ { |
+ $locale = ($locale ?: $this->getLocale()); |
+ $translation = $this->getTranslatedMessage($message, $locale, $textDomain); |
+ |
+ if ($translation !== null && $translation !== '') { |
+ return $translation; |
+ } |
+ |
+ if (null !== ($fallbackLocale = $this->getFallbackLocale()) |
+ && $locale !== $fallbackLocale |
+ ) { |
+ return $this->translate($message, $textDomain, $fallbackLocale); |
+ } |
+ |
+ return $message; |
+ } |
+ |
+ /** |
+ * Translate a plural message. |
+ * |
+ * @param string $singular |
+ * @param string $plural |
+ * @param int $number |
+ * @param string $textDomain |
+ * @param string|null $locale |
+ * @return string |
+ * @throws Exception\OutOfBoundsException |
+ */ |
+ public function translatePlural( |
+ $singular, |
+ $plural, |
+ $number, |
+ $textDomain = 'default', |
+ $locale = null |
+ ) { |
+ $locale = $locale ?: $this->getLocale(); |
+ $translation = $this->getTranslatedMessage($singular, $locale, $textDomain); |
+ |
+ if ($translation === null || $translation === '') { |
+ if (null !== ($fallbackLocale = $this->getFallbackLocale()) |
+ && $locale !== $fallbackLocale |
+ ) { |
+ return $this->translatePlural( |
+ $singular, |
+ $plural, |
+ $number, |
+ $textDomain, |
+ $fallbackLocale |
+ ); |
+ } |
+ |
+ return ($number == 1 ? $singular : $plural); |
+ } |
+ |
+ $index = $this->messages[$textDomain][$locale] |
+ ->getPluralRule() |
+ ->evaluate($number); |
+ |
+ if (!isset($translation[$index])) { |
+ throw new Exception\OutOfBoundsException(sprintf( |
+ 'Provided index %d does not exist in plural array', $index |
+ )); |
+ } |
+ |
+ return $translation[$index]; |
+ } |
+ |
+ /** |
+ * Get a translated message. |
+ * |
+ * @param string $message |
+ * @param string $locale |
+ * @param string $textDomain |
+ * @return string|null |
+ */ |
+ protected function getTranslatedMessage( |
+ $message, |
+ $locale = null, |
+ $textDomain = 'default' |
+ ) { |
+ if ($message === '') { |
+ return ''; |
+ } |
+ |
+ if (!isset($this->messages[$textDomain][$locale])) { |
+ $this->loadMessages($textDomain, $locale); |
+ } |
+ |
+ if (!isset($this->messages[$textDomain][$locale][$message])) { |
+ return null; |
+ } |
+ |
+ return $this->messages[$textDomain][$locale][$message]; |
+ } |
+ |
+ /** |
+ * Add a translation file. |
+ * |
+ * @param string $type |
+ * @param string $filename |
+ * @param string $textDomain |
+ * @param string $locale |
+ * @return Translator |
+ */ |
+ public function addTranslationFile( |
+ $type, |
+ $filename, |
+ $textDomain = 'default', |
+ $locale = null |
+ ) { |
+ $locale = $locale ?: '*'; |
+ |
+ if (!isset($this->files[$textDomain])) { |
+ $this->files[$textDomain] = array(); |
+ } |
+ |
+ $this->files[$textDomain][$locale][] = array( |
+ 'type' => $type, |
+ 'filename' => $filename, |
+ ); |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * Add multiple translations with a file pattern. |
+ * |
+ * @param string $type |
+ * @param string $baseDir |
+ * @param string $pattern |
+ * @param string $textDomain |
+ * @return Translator |
+ */ |
+ public function addTranslationFilePattern( |
+ $type, |
+ $baseDir, |
+ $pattern, |
+ $textDomain = 'default' |
+ ) { |
+ if (!isset($this->patterns[$textDomain])) { |
+ $this->patterns[$textDomain] = array(); |
+ } |
+ |
+ $this->patterns[$textDomain][] = array( |
+ 'type' => $type, |
+ 'baseDir' => rtrim($baseDir, '/'), |
+ 'pattern' => $pattern, |
+ ); |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * Add remote translations. |
+ * |
+ * @param string $type |
+ * @param string $textDomain |
+ * @return Translator |
+ */ |
+ public function addRemoteTranslations($type, $textDomain = 'default') |
+ { |
+ if (!isset($this->remote[$textDomain])) { |
+ $this->remote[$textDomain] = array(); |
+ } |
+ |
+ $this->remote[$textDomain][] = $type; |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * Load messages for a given language and domain. |
+ * |
+ * @param string $textDomain |
+ * @param string $locale |
+ * @throws Exception\RuntimeException |
+ * @return void |
+ */ |
+ protected function loadMessages($textDomain, $locale) |
+ { |
+ if (!isset($this->messages[$textDomain])) { |
+ $this->messages[$textDomain] = array(); |
+ } |
+ |
+ if (null !== ($cache = $this->getCache())) { |
+ $cacheId = 'Zend_I18n_Translator_Messages_' . md5($textDomain . $locale); |
+ |
+ if (null !== ($result = $cache->getItem($cacheId))) { |
+ $this->messages[$textDomain][$locale] = $result; |
+ |
+ return; |
+ } |
+ } |
+ |
+ $hasToCache = false; |
+ |
+ // Try to load from remote sources |
+ if (isset($this->remote[$textDomain])) { |
+ foreach ($this->remote[$textDomain] as $loaderType) { |
+ $loader = $this->getPluginManager()->get($loaderType); |
+ |
+ if (!$loader instanceof RemoteLoaderInterface) { |
+ throw new Exception\RuntimeException('Specified loader is not a remote loader'); |
+ } |
+ |
+ if (isset($this->messages[$textDomain][$locale])) { |
+ $this->messages[$textDomain][$locale]->exchangeArray(array_merge( |
+ (array) $this->messages[$textDomain][$locale], |
+ (array) $loader->load($locale, $textDomain) |
+ )); |
+ } else { |
+ $this->messages[$textDomain][$locale] = $loader->load($locale, $textDomain); |
+ } |
+ $hasToCache = true; |
+ } |
+ } |
+ |
+ // Try to load from pattern |
+ if (isset($this->patterns[$textDomain])) { |
+ foreach ($this->patterns[$textDomain] as $pattern) { |
+ $filename = $pattern['baseDir'] . '/' . sprintf($pattern['pattern'], $locale); |
+ |
+ if (is_file($filename)) { |
+ $loader = $this->getPluginManager()->get($pattern['type']); |
+ |
+ if (!$loader instanceof FileLoaderInterface) { |
+ throw new Exception\RuntimeException('Specified loader is not a file loader'); |
+ } |
+ |
+ if (isset($this->messages[$textDomain][$locale])) { |
+ $this->messages[$textDomain][$locale]->exchangeArray(array_merge( |
+ (array) $this->messages[$textDomain][$locale], |
+ (array) $loader->load($locale, $filename) |
+ )); |
+ } else { |
+ $this->messages[$textDomain][$locale] = $loader->load($locale, $filename); |
+ } |
+ $hasToCache = true; |
+ } |
+ } |
+ } |
+ |
+ // Try to load from concrete files |
+ foreach (array($locale, '*') as $currentLocale) { |
+ if (!isset($this->files[$textDomain][$currentLocale])) { |
+ continue; |
+ } |
+ foreach ($this->files[$textDomain][$currentLocale] as $file) { |
+ $loader = $this->getPluginManager()->get($file['type']); |
+ |
+ if (!$loader instanceof FileLoaderInterface) { |
+ throw new Exception\RuntimeException('Specified loader is not a file loader'); |
+ } |
+ |
+ if (isset($this->messages[$textDomain][$locale])) { |
+ $this->messages[$textDomain][$locale]->exchangeArray(array_merge( |
+ (array) $this->messages[$textDomain][$locale], |
+ (array) $loader->load($locale, $file['filename']) |
+ )); |
+ } else { |
+ $this->messages[$textDomain][$locale] = $loader->load($locale, $file['filename']); |
+ } |
+ $hasToCache = true; |
+ } |
+ unset($this->files[$textDomain][$currentLocale]); |
+ } |
+ |
+ // Cache the loaded text domain |
+ if ($hasToCache && $cache !== null) { |
+ $cache->setItem($cacheId, $this->messages[$textDomain][$locale]); |
+ } |
+ } |
+} |
Index: trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/TextDomain.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/TextDomain.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/TextDomain.php (revision 80) |
@@ -0,0 +1,54 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\I18n\Translator; |
+ |
+use ArrayObject; |
+use Zend\I18n\Translator\Plural\Rule as PluralRule; |
+ |
+/** |
+ * Text domain. |
+ */ |
+class TextDomain extends ArrayObject |
+{ |
+ /** |
+ * Plural rule. |
+ * |
+ * @var PluralRule |
+ */ |
+ protected $pluralRule; |
+ |
+ /** |
+ * Set the plural rule |
+ * |
+ * @param PluralRule $rule |
+ * @return TextDomain |
+ */ |
+ public function setPluralRule(PluralRule $rule) |
+ { |
+ $this->pluralRule = $rule; |
+ return $this; |
+ } |
+ |
+ /** |
+ * Get the plural rule. |
+ * |
+ * Lazy loads a default rule if none already registered |
+ * |
+ * @return PluralRule |
+ */ |
+ public function getPluralRule() |
+ { |
+ if ($this->pluralRule === null) { |
+ $this->setPluralRule(PluralRule::fromString('nplurals=2; plural=n==1')); |
+ } |
+ |
+ return $this->pluralRule; |
+ } |
+} |
Index: trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Loader/Gettext.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Loader/Gettext.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Loader/Gettext.php (revision 80) |
@@ -0,0 +1,188 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\I18n\Translator\Loader; |
+ |
+use Zend\I18n\Exception; |
+use Zend\I18n\Translator\Plural\Rule as PluralRule; |
+use Zend\I18n\Translator\TextDomain; |
+use Zend\Stdlib\ErrorHandler; |
+ |
+/** |
+ * Gettext loader. |
+ */ |
+class Gettext implements FileLoaderInterface |
+{ |
+ /** |
+ * Current file pointer. |
+ * |
+ * @var resource |
+ */ |
+ protected $file; |
+ |
+ /** |
+ * Whether the current file is little endian. |
+ * |
+ * @var bool |
+ */ |
+ protected $littleEndian; |
+ |
+ /** |
+ * load(): defined by FileLoaderInterface. |
+ * |
+ * @see FileLoaderInterface::load() |
+ * @param string $locale |
+ * @param string $filename |
+ * @return TextDomain |
+ * @throws Exception\InvalidArgumentException |
+ */ |
+ public function load($locale, $filename) |
+ { |
+ if (!is_file($filename) || !is_readable($filename)) { |
+ throw new Exception\InvalidArgumentException(sprintf( |
+ 'Could not open file %s for reading', |
+ $filename |
+ )); |
+ } |
+ |
+ $textDomain = new TextDomain(); |
+ |
+ ErrorHandler::start(); |
+ $this->file = fopen($filename, 'rb'); |
+ $error = ErrorHandler::stop(); |
+ if (false === $this->file) { |
+ throw new Exception\InvalidArgumentException(sprintf( |
+ 'Could not open file %s for reading', |
+ $filename |
+ ), 0, $error); |
+ } |
+ |
+ // Verify magic number |
+ $magic = fread($this->file, 4); |
+ |
+ if ($magic == "\x95\x04\x12\xde") { |
+ $this->littleEndian = false; |
+ } elseif ($magic == "\xde\x12\x04\x95") { |
+ $this->littleEndian = true; |
+ } else { |
+ fclose($this->file); |
+ throw new Exception\InvalidArgumentException(sprintf( |
+ '%s is not a valid gettext file', |
+ $filename |
+ )); |
+ } |
+ |
+ // Verify major revision (only 0 and 1 supported) |
+ $majorRevision = ($this->readInteger() >> 16); |
+ |
+ if ($majorRevision !== 0 && $majorRevision !== 1) { |
+ fclose($this->file); |
+ throw new Exception\InvalidArgumentException(sprintf( |
+ '%s has an unknown major revision', |
+ $filename |
+ )); |
+ } |
+ |
+ // Gather main information |
+ $numStrings = $this->readInteger(); |
+ $originalStringTableOffset = $this->readInteger(); |
+ $translationStringTableOffset = $this->readInteger(); |
+ |
+ // Usually there follow size and offset of the hash table, but we have |
+ // no need for it, so we skip them. |
+ fseek($this->file, $originalStringTableOffset); |
+ $originalStringTable = $this->readIntegerList(2 * $numStrings); |
+ |
+ fseek($this->file, $translationStringTableOffset); |
+ $translationStringTable = $this->readIntegerList(2 * $numStrings); |
+ |
+ // Read in all translations |
+ for ($current = 0; $current < $numStrings; $current++) { |
+ $sizeKey = $current * 2 + 1; |
+ $offsetKey = $current * 2 + 2; |
+ $originalStringSize = $originalStringTable[$sizeKey]; |
+ $originalStringOffset = $originalStringTable[$offsetKey]; |
+ $translationStringSize = $translationStringTable[$sizeKey]; |
+ $translationStringOffset = $translationStringTable[$offsetKey]; |
+ |
+ $originalString = array(''); |
+ if ($originalStringSize > 0) { |
+ fseek($this->file, $originalStringOffset); |
+ $originalString = explode("\0", fread($this->file, $originalStringSize)); |
+ } |
+ |
+ if ($translationStringSize > 0) { |
+ fseek($this->file, $translationStringOffset); |
+ $translationString = explode("\0", fread($this->file, $translationStringSize)); |
+ |
+ if (count($originalString) > 1 && count($translationString) > 1) { |
+ $textDomain[$originalString[0]] = $translationString; |
+ |
+ array_shift($originalString); |
+ |
+ foreach ($originalString as $string) { |
+ $textDomain[$string] = ''; |
+ } |
+ } else { |
+ $textDomain[$originalString[0]] = $translationString[0]; |
+ } |
+ } |
+ } |
+ |
+ // Read header entries |
+ if (array_key_exists('', $textDomain)) { |
+ $rawHeaders = explode("\n", trim($textDomain[''])); |
+ |
+ foreach ($rawHeaders as $rawHeader) { |
+ list($header, $content) = explode(':', $rawHeader, 2); |
+ |
+ if (trim(strtolower($header)) === 'plural-forms') { |
+ $textDomain->setPluralRule(PluralRule::fromString($content)); |
+ } |
+ } |
+ |
+ unset($textDomain['']); |
+ } |
+ |
+ fclose($this->file); |
+ |
+ return $textDomain; |
+ } |
+ |
+ /** |
+ * Read a single integer from the current file. |
+ * |
+ * @return integer |
+ */ |
+ protected function readInteger() |
+ { |
+ if ($this->littleEndian) { |
+ $result = unpack('Vint', fread($this->file, 4)); |
+ } else { |
+ $result = unpack('Nint', fread($this->file, 4)); |
+ } |
+ |
+ return $result['int']; |
+ } |
+ |
+ /** |
+ * Read an integer from the current file. |
+ * |
+ * @param integer $num |
+ * @return integer |
+ */ |
+ protected function readIntegerList($num) |
+ { |
+ if ($this->littleEndian) { |
+ return unpack('V' . $num, fread($this->file, 4 * $num)); |
+ } |
+ |
+ return unpack('N' . $num, fread($this->file, 4 * $num)); |
+ } |
+} |
Index: trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Loader/FileLoaderInterface.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Loader/FileLoaderInterface.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/Loader/FileLoaderInterface.php (revision 80) |
@@ -0,0 +1,25 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\I18n\Translator\Loader; |
+ |
+/** |
+ * File loader interface. |
+ */ |
+interface FileLoaderInterface |
+{ |
+ /** |
+ * Load translations from a file. |
+ * |
+ * @param string $locale |
+ * @param string $filename |
+ * @return \Zend\I18n\Translator\TextDomain|null |
+ */ |
+ public function load($locale, $filename); |
+} |
Index: trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/LoaderPluginManager.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/LoaderPluginManager.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/I18n/Translator/LoaderPluginManager.php (revision 80) |
@@ -0,0 +1,58 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\I18n\Translator; |
+ |
+use Zend\I18n\Exception; |
+use Zend\ServiceManager\AbstractPluginManager; |
+ |
+/** |
+ * Plugin manager implementation for translation loaders. |
+ * |
+ * Enforces that loaders retrieved are either instances of |
+ * Loader\FileLoaderInterface or Loader\RemoteLoaderInterface. Additionally, |
+ * it registers a number of default loaders. |
+ */ |
+class LoaderPluginManager extends AbstractPluginManager |
+{ |
+ /** |
+ * Default set of loaders. |
+ * |
+ * @var array |
+ */ |
+ protected $invokableClasses = array( |
+ 'gettext' => 'Zend\I18n\Translator\Loader\Gettext', |
+ 'ini' => 'Zend\I18n\Translator\Loader\Ini', |
+ 'phparray' => 'Zend\I18n\Translator\Loader\PhpArray', |
+ ); |
+ |
+ /** |
+ * Validate the plugin. |
+ * |
+ * Checks that the filter loaded is an instance of |
+ * Loader\FileLoaderInterface or Loader\RemoteLoaderInterface. |
+ * |
+ * @param mixed $plugin |
+ * @return void |
+ * @throws Exception\RuntimeException if invalid |
+ */ |
+ public function validatePlugin($plugin) |
+ { |
+ if ($plugin instanceof Loader\FileLoaderInterface || $plugin instanceof Loader\RemoteLoaderInterface) { |
+ // we're okay |
+ return; |
+ } |
+ |
+ throw new Exception\RuntimeException(sprintf( |
+ 'Plugin of type %s is invalid; must implement %s\Loader\FileLoaderInterface or %s\Loader\RemoteLoaderInterface', |
+ (is_object($plugin) ? get_class($plugin) : gettype($plugin)), |
+ __NAMESPACE__ |
+ )); |
+ } |
+} |
Index: trunk/ZendFramework-2.1.2/library/Zend/Stdlib/ErrorHandler.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/Stdlib/ErrorHandler.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/Stdlib/ErrorHandler.php (revision 80) |
@@ -0,0 +1,115 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\Stdlib; |
+ |
+use ErrorException; |
+ |
+/** |
+ * ErrorHandler that can be used to catch internal PHP errors |
+ * and convert to a ErrorException instance. |
+ */ |
+abstract class ErrorHandler |
+{ |
+ /** |
+ * Active stack |
+ * |
+ * @var array |
+ */ |
+ protected static $stack = array(); |
+ |
+ /** |
+ * Check if this error handler is active |
+ * |
+ * @return boolean |
+ */ |
+ public static function started() |
+ { |
+ return (bool) static::getNestedLevel(); |
+ } |
+ |
+ /** |
+ * Get the current nested level |
+ * |
+ * @return int |
+ */ |
+ public static function getNestedLevel() |
+ { |
+ return count(static::$stack); |
+ } |
+ |
+ /** |
+ * Starting the error handler |
+ * |
+ * @param int $errorLevel |
+ */ |
+ public static function start($errorLevel = \E_WARNING) |
+ { |
+ if (!static::$stack) { |
+ set_error_handler(array(get_called_class(), 'addError'), $errorLevel); |
+ } |
+ |
+ static::$stack[] = null; |
+ } |
+ |
+ /** |
+ * Stopping the error handler |
+ * |
+ * @param bool $throw Throw the ErrorException if any |
+ * @return null|ErrorException |
+ * @throws ErrorException If an error has been catched and $throw is true |
+ */ |
+ public static function stop($throw = false) |
+ { |
+ $errorException = null; |
+ |
+ if (static::$stack) { |
+ $errorException = array_pop(static::$stack); |
+ |
+ if (!static::$stack) { |
+ restore_error_handler(); |
+ } |
+ |
+ if ($errorException && $throw) { |
+ throw $errorException; |
+ } |
+ } |
+ |
+ return $errorException; |
+ } |
+ |
+ /** |
+ * Stop all active handler |
+ * |
+ * @return void |
+ */ |
+ public static function clean() |
+ { |
+ if (static::$stack) { |
+ restore_error_handler(); |
+ } |
+ |
+ static::$stack = array(); |
+ } |
+ |
+ /** |
+ * Add an error to the stack |
+ * |
+ * @param int $errno |
+ * @param string $errstr |
+ * @param string $errfile |
+ * @param int $errline |
+ * @return void |
+ */ |
+ public static function addError($errno, $errstr = '', $errfile = '', $errline = 0) |
+ { |
+ $stack = & static::$stack[ count(static::$stack) - 1 ]; |
+ $stack = new ErrorException($errstr, 0, $errno, $errfile, $errline, $stack); |
+ } |
+} |
Index: trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceManager.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceManager.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceManager.php (revision 80) |
@@ -0,0 +1,986 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\ServiceManager; |
+ |
+use ReflectionClass; |
+ |
+class ServiceManager implements ServiceLocatorInterface |
+{ |
+ |
+ /**@#+ |
+ * Constants |
+ */ |
+ const SCOPE_PARENT = 'parent'; |
+ const SCOPE_CHILD = 'child'; |
+ /**@#-*/ |
+ |
+ /** |
+ * Lookup for canonicalized names. |
+ * |
+ * @var array |
+ */ |
+ protected $canonicalNames = array(); |
+ |
+ /** |
+ * @var bool |
+ */ |
+ protected $allowOverride = false; |
+ |
+ /** |
+ * @var array |
+ */ |
+ protected $invokableClasses = array(); |
+ |
+ /** |
+ * @var string|callable|\Closure|FactoryInterface[] |
+ */ |
+ protected $factories = array(); |
+ |
+ /** |
+ * @var AbstractFactoryInterface[] |
+ */ |
+ protected $abstractFactories = array(); |
+ |
+ /** |
+ * @var array |
+ */ |
+ protected $pendingAbstractFactoryRequests = array(); |
+ |
+ /** |
+ * @var array |
+ */ |
+ protected $shared = array(); |
+ |
+ /** |
+ * Registered services and cached values |
+ * |
+ * @var array |
+ */ |
+ protected $instances = array(); |
+ |
+ /** |
+ * @var array |
+ */ |
+ protected $aliases = array(); |
+ |
+ /** |
+ * @var array |
+ */ |
+ protected $initializers = array(); |
+ |
+ /** |
+ * @var ServiceManager[] |
+ */ |
+ protected $peeringServiceManagers = array(); |
+ |
+ /** |
+ * Whether or not to share by default |
+ * |
+ * @var bool |
+ */ |
+ protected $shareByDefault = true; |
+ |
+ /** |
+ * @var bool |
+ */ |
+ protected $retrieveFromPeeringManagerFirst = false; |
+ |
+ /** |
+ * @var bool Track whether not to throw exceptions during create() |
+ */ |
+ protected $throwExceptionInCreate = true; |
+ |
+ /** |
+ * @var array map of characters to be replaced through strtr |
+ */ |
+ protected $canonicalNamesReplacements = array('-' => '', '_' => '', ' ' => '', '\\' => '', '/' => ''); |
+ |
+ /** |
+ * Constructor |
+ * |
+ * @param ConfigInterface $config |
+ */ |
+ public function __construct(ConfigInterface $config = null) |
+ { |
+ if ($config) { |
+ $config->configureServiceManager($this); |
+ } |
+ } |
+ |
+ /** |
+ * Set allow override |
+ * |
+ * @param $allowOverride |
+ * @return ServiceManager |
+ */ |
+ public function setAllowOverride($allowOverride) |
+ { |
+ $this->allowOverride = (bool) $allowOverride; |
+ return $this; |
+ } |
+ |
+ /** |
+ * Get allow override |
+ * |
+ * @return bool |
+ */ |
+ public function getAllowOverride() |
+ { |
+ return $this->allowOverride; |
+ } |
+ |
+ /** |
+ * Set flag indicating whether services are shared by default |
+ * |
+ * @param bool $shareByDefault |
+ * @return ServiceManager |
+ * @throws Exception\RuntimeException if allowOverride is false |
+ */ |
+ public function setShareByDefault($shareByDefault) |
+ { |
+ if ($this->allowOverride === false) { |
+ throw new Exception\RuntimeException(sprintf( |
+ '%s: cannot alter default shared service setting; container is marked immutable (allow_override is false)', |
+ __METHOD__ |
+ )); |
+ } |
+ $this->shareByDefault = (bool) $shareByDefault; |
+ return $this; |
+ } |
+ |
+ /** |
+ * Are services shared by default? |
+ * |
+ * @return bool |
+ */ |
+ public function shareByDefault() |
+ { |
+ return $this->shareByDefault; |
+ } |
+ |
+ /** |
+ * Set throw exceptions in create |
+ * |
+ * @param bool $throwExceptionInCreate |
+ * @return ServiceManager |
+ */ |
+ public function setThrowExceptionInCreate($throwExceptionInCreate) |
+ { |
+ $this->throwExceptionInCreate = $throwExceptionInCreate; |
+ return $this; |
+ } |
+ |
+ /** |
+ * Get throw exceptions in create |
+ * |
+ * @return bool |
+ */ |
+ public function getThrowExceptionInCreate() |
+ { |
+ return $this->throwExceptionInCreate; |
+ } |
+ |
+ /** |
+ * Set flag indicating whether to pull from peering manager before attempting creation |
+ * |
+ * @param bool $retrieveFromPeeringManagerFirst |
+ * @return ServiceManager |
+ */ |
+ public function setRetrieveFromPeeringManagerFirst($retrieveFromPeeringManagerFirst = true) |
+ { |
+ $this->retrieveFromPeeringManagerFirst = (bool) $retrieveFromPeeringManagerFirst; |
+ return $this; |
+ } |
+ |
+ /** |
+ * Should we retrieve from the peering manager prior to attempting to create a service? |
+ * |
+ * @return bool |
+ */ |
+ public function retrieveFromPeeringManagerFirst() |
+ { |
+ return $this->retrieveFromPeeringManagerFirst; |
+ } |
+ |
+ /** |
+ * Set invokable class |
+ * |
+ * @param string $name |
+ * @param string $invokableClass |
+ * @param bool $shared |
+ * @return ServiceManager |
+ * @throws Exception\InvalidServiceNameException |
+ */ |
+ public function setInvokableClass($name, $invokableClass, $shared = null) |
+ { |
+ $cName = $this->canonicalizeName($name); |
+ |
+ if ($this->has(array($cName, $name), false)) { |
+ if ($this->allowOverride === false) { |
+ throw new Exception\InvalidServiceNameException(sprintf( |
+ 'A service by the name or alias "%s" already exists and cannot be overridden; please use an alternate name', |
+ $cName |
+ )); |
+ } |
+ $this->unregisterService($cName); |
+ } |
+ |
+ if ($shared === null) { |
+ $shared = $this->shareByDefault(); |
+ } |
+ |
+ $this->invokableClasses[$cName] = $invokableClass; |
+ $this->shared[$cName] = (bool) $shared; |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * Set factory |
+ * |
+ * @param string $name |
+ * @param string|FactoryInterface|callable $factory |
+ * @param bool $shared |
+ * @return ServiceManager |
+ * @throws Exception\InvalidArgumentException |
+ * @throws Exception\InvalidServiceNameException |
+ */ |
+ public function setFactory($name, $factory, $shared = null) |
+ { |
+ $cName = $this->canonicalizeName($name); |
+ |
+ if (!is_string($factory) && !$factory instanceof FactoryInterface && !is_callable($factory)) { |
+ throw new Exception\InvalidArgumentException( |
+ 'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.' |
+ ); |
+ } |
+ |
+ if ($this->has(array($cName, $name), false)) { |
+ if ($this->allowOverride === false) { |
+ throw new Exception\InvalidServiceNameException(sprintf( |
+ 'A service by the name or alias "%s" already exists and cannot be overridden, please use an alternate name', |
+ $cName |
+ )); |
+ } |
+ $this->unregisterService($cName); |
+ } |
+ |
+ if ($shared === null) { |
+ $shared = $this->shareByDefault(); |
+ } |
+ |
+ $this->factories[$cName] = $factory; |
+ $this->shared[$cName] = (bool) $shared; |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * Add abstract factory |
+ * |
+ * @param AbstractFactoryInterface|string $factory |
+ * @param bool $topOfStack |
+ * @return ServiceManager |
+ * @throws Exception\InvalidArgumentException if the abstract factory is invalid |
+ */ |
+ public function addAbstractFactory($factory, $topOfStack = true) |
+ { |
+ if (!is_string($factory) && !$factory instanceof AbstractFactoryInterface) { |
+ throw new Exception\InvalidArgumentException( |
+ 'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.' |
+ ); |
+ } |
+ if (is_string($factory)) { |
+ if (!class_exists($factory, true)) { |
+ throw new Exception\InvalidArgumentException( |
+ 'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.' |
+ ); |
+ } |
+ $refl = new ReflectionClass($factory); |
+ if (!$refl->implementsInterface(__NAMESPACE__ . '\\AbstractFactoryInterface')) { |
+ throw new Exception\InvalidArgumentException( |
+ 'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.' |
+ ); |
+ } |
+ } |
+ |
+ if ($topOfStack) { |
+ array_unshift($this->abstractFactories, $factory); |
+ } else { |
+ array_push($this->abstractFactories, $factory); |
+ } |
+ return $this; |
+ } |
+ |
+ /** |
+ * Add initializer |
+ * |
+ * @param callable|InitializerInterface $initializer |
+ * @param bool $topOfStack |
+ * @return ServiceManager |
+ * @throws Exception\InvalidArgumentException |
+ */ |
+ public function addInitializer($initializer, $topOfStack = true) |
+ { |
+ if (!is_callable($initializer) && !$initializer instanceof InitializerInterface) { |
+ if (!is_string($initializer) |
+ || !$this->isSubclassOf($initializer, __NAMESPACE__ . '\InitializerInterface') |
+ ) { |
+ throw new Exception\InvalidArgumentException('$initializer should be callable.'); |
+ } |
+ $initializer = new $initializer; |
+ } |
+ |
+ if ($topOfStack) { |
+ array_unshift($this->initializers, $initializer); |
+ } else { |
+ array_push($this->initializers, $initializer); |
+ } |
+ return $this; |
+ } |
+ |
+ /** |
+ * Register a service with the locator |
+ * |
+ * @param string $name |
+ * @param mixed $service |
+ * @return ServiceManager |
+ * @throws Exception\InvalidServiceNameException |
+ */ |
+ public function setService($name, $service) |
+ { |
+ $cName = $this->canonicalizeName($name); |
+ |
+ if ($this->has($cName, false)) { |
+ if ($this->allowOverride === false) { |
+ throw new Exception\InvalidServiceNameException(sprintf( |
+ '%s: A service by the name "%s" or alias already exists and cannot be overridden, please use an alternate name.', |
+ __METHOD__, |
+ $name |
+ )); |
+ } |
+ $this->unregisterService($cName); |
+ } |
+ |
+ $this->instances[$cName] = $service; |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * @param string $name |
+ * @param bool $isShared |
+ * @return ServiceManager |
+ * @throws Exception\ServiceNotFoundException |
+ */ |
+ public function setShared($name, $isShared) |
+ { |
+ $cName = $this->canonicalizeName($name); |
+ |
+ if ( |
+ !isset($this->invokableClasses[$cName]) |
+ && !isset($this->factories[$cName]) |
+ && !$this->canCreateFromAbstractFactory($cName, $name) |
+ ) { |
+ throw new Exception\ServiceNotFoundException(sprintf( |
+ '%s: A service by the name "%s" was not found and could not be marked as shared', |
+ __METHOD__, |
+ $name |
+ )); |
+ } |
+ |
+ $this->shared[$cName] = (bool) $isShared; |
+ return $this; |
+ } |
+ |
+ /** |
+ * Retrieve a registered instance |
+ * |
+ * @param string $name |
+ * @param bool $usePeeringServiceManagers |
+ * @throws Exception\ServiceNotFoundException |
+ * @return object|array |
+ */ |
+ public function get($name, $usePeeringServiceManagers = true) |
+ { |
+ $cName = $this->canonicalizeName($name); |
+ $isAlias = false; |
+ |
+ if ($this->hasAlias($cName)) { |
+ $isAlias = true; |
+ |
+ do { |
+ $cName = $this->aliases[$cName]; |
+ } while ($this->hasAlias($cName)); |
+ } |
+ |
+ $instance = null; |
+ $retrieveFromPeeringManagerFirst = $this->retrieveFromPeeringManagerFirst(); |
+ |
+ if ($usePeeringServiceManagers && $retrieveFromPeeringManagerFirst) { |
+ $instance = $this->retrieveFromPeeringManager($name); |
+ |
+ if(null !== $instance) { |
+ return $instance; |
+ } |
+ } |
+ |
+ if (isset($this->instances[$cName])) { |
+ return $this->instances[$cName]; |
+ } |
+ |
+ if (!$instance) { |
+ if ($this->canCreate(array($cName, $name))) { |
+ $instance = $this->create(array($cName, $name)); |
+ } elseif ($usePeeringServiceManagers && !$retrieveFromPeeringManagerFirst) { |
+ $instance = $this->retrieveFromPeeringManager($name); |
+ } |
+ } |
+ |
+ // Still no instance? raise an exception |
+ if ($instance === null && !is_array($instance)) { |
+ if ($isAlias) { |
+ throw new Exception\ServiceNotFoundException(sprintf( |
+ 'An alias "%s" was requested but no service could be found.', |
+ $name |
+ )); |
+ } |
+ |
+ throw new Exception\ServiceNotFoundException(sprintf( |
+ '%s was unable to fetch or create an instance for %s', |
+ __METHOD__, |
+ $name |
+ )); |
+ } |
+ |
+ if ( |
+ ($this->shareByDefault() && !isset($this->shared[$cName])) |
+ || (isset($this->shared[$cName]) && $this->shared[$cName] === true) |
+ ) { |
+ $this->instances[$cName] = $instance; |
+ } |
+ |
+ return $instance; |
+ } |
+ |
+ /** |
+ * Create an instance |
+ * |
+ * @param string|array $name |
+ * @return bool|object |
+ * @throws Exception\ServiceNotFoundException |
+ * @throws Exception\ServiceNotCreatedException |
+ */ |
+ public function create($name) |
+ { |
+ $instance = false; |
+ |
+ if (is_array($name)) { |
+ list($cName, $rName) = $name; |
+ } else { |
+ $rName = $name; |
+ $cName = $this->canonicalizeName($rName); |
+ } |
+ |
+ |
+ if (isset($this->factories[$cName])) { |
+ $instance = $this->createFromFactory($cName, $rName); |
+ } |
+ |
+ if ($instance === false && isset($this->invokableClasses[$cName])) { |
+ $instance = $this->createFromInvokable($cName, $rName); |
+ } |
+ |
+ if ($instance === false && $this->canCreateFromAbstractFactory($cName, $rName)) { |
+ $instance = $this->createFromAbstractFactory($cName, $rName); |
+ } |
+ |
+ if ($this->throwExceptionInCreate == true && $instance === false) { |
+ throw new Exception\ServiceNotFoundException(sprintf( |
+ 'No valid instance was found for %s%s', |
+ $cName, |
+ ($rName ? '(alias: ' . $rName . ')' : '') |
+ )); |
+ } |
+ |
+ foreach ($this->initializers as $initializer) { |
+ if ($initializer instanceof InitializerInterface) { |
+ $initializer->initialize($instance, $this); |
+ } elseif (is_object($initializer) && is_callable($initializer)) { |
+ $initializer($instance, $this); |
+ } else { |
+ call_user_func($initializer, $instance, $this); |
+ } |
+ } |
+ |
+ return $instance; |
+ } |
+ |
+ /** |
+ * Determine if we can create an instance. |
+ * |
+ * @param string|array $name |
+ * @param bool $checkAbstractFactories |
+ * @return bool |
+ */ |
+ public function canCreate($name, $checkAbstractFactories = true) |
+ { |
+ if (is_array($name)) { |
+ list($cName, $rName) = $name; |
+ } else { |
+ $rName = $name; |
+ $cName = $this->canonicalizeName($rName); |
+ } |
+ |
+ if ( |
+ isset($this->invokableClasses[$cName]) |
+ || isset($this->factories[$cName]) |
+ || isset($this->aliases[$cName]) |
+ || isset($this->instances[$cName]) |
+ ) { |
+ return true; |
+ } |
+ |
+ if ($checkAbstractFactories && $this->canCreateFromAbstractFactory($cName, $rName)) { |
+ return true; |
+ } |
+ |
+ return false; |
+ } |
+ |
+ /** |
+ * @param string|array $name |
+ * @param bool $checkAbstractFactories |
+ * @param bool $usePeeringServiceManagers |
+ * @return bool |
+ */ |
+ public function has($name, $checkAbstractFactories = true, $usePeeringServiceManagers = true) |
+ { |
+ if (is_array($name)) { |
+ list($cName, $rName) = $name; |
+ } else { |
+ $rName = $name; |
+ $cName = $this->canonicalizeName($rName); |
+ } |
+ |
+ if ($this->canCreate(array($cName, $rName), $checkAbstractFactories)) { |
+ return true; |
+ } |
+ |
+ if ($usePeeringServiceManagers) { |
+ foreach ($this->peeringServiceManagers as $peeringServiceManager) { |
+ if ($peeringServiceManager->has($rName)) { |
+ return true; |
+ } |
+ } |
+ } |
+ |
+ return false; |
+ } |
+ |
+ /** |
+ * Determine if we can create an instance from an abstract factory. |
+ * |
+ * @param string $cName |
+ * @param string $rName |
+ * @return bool |
+ */ |
+ public function canCreateFromAbstractFactory($cName, $rName) |
+ { |
+ // check abstract factories |
+ foreach ($this->abstractFactories as $index => $abstractFactory) { |
+ // Support string abstract factory class names |
+ if (is_string($abstractFactory) && class_exists($abstractFactory, true)) { |
+ $this->abstractFactories[$index] = $abstractFactory = new $abstractFactory(); |
+ } |
+ |
+ if ( |
+ isset($this->pendingAbstractFactoryRequests[get_class($abstractFactory)]) |
+ && $this->pendingAbstractFactoryRequests[get_class($abstractFactory)] == $rName |
+ ) { |
+ return false; |
+ } |
+ |
+ if ($abstractFactory->canCreateServiceWithName($this, $cName, $rName)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * @param string $alias |
+ * @param string $nameOrAlias |
+ * @return ServiceManager |
+ * @throws Exception\ServiceNotFoundException |
+ * @throws Exception\InvalidServiceNameException |
+ */ |
+ public function setAlias($alias, $nameOrAlias) |
+ { |
+ if (!is_string($alias) || !is_string($nameOrAlias)) { |
+ throw new Exception\InvalidServiceNameException('Service or alias names must be strings.'); |
+ } |
+ |
+ $cAlias = $this->canonicalizeName($alias); |
+ $nameOrAlias = $this->canonicalizeName($nameOrAlias); |
+ |
+ if ($alias == '' || $nameOrAlias == '') { |
+ throw new Exception\InvalidServiceNameException('Invalid service name alias'); |
+ } |
+ |
+ if ($this->allowOverride === false && $this->has(array($cAlias, $alias), false)) { |
+ throw new Exception\InvalidServiceNameException(sprintf( |
+ 'An alias by the name "%s" or "%s" already exists', |
+ $cAlias, |
+ $alias |
+ )); |
+ } |
+ |
+ $this->aliases[$cAlias] = $nameOrAlias; |
+ return $this; |
+ } |
+ |
+ /** |
+ * Determine if we have an alias |
+ * |
+ * @param string $alias |
+ * @return bool |
+ */ |
+ public function hasAlias($alias) |
+ { |
+ $alias = $this->canonicalizeName($alias); |
+ return (isset($this->aliases[$alias])); |
+ } |
+ |
+ /** |
+ * Create scoped service manager |
+ * |
+ * @param string $peering |
+ * @return ServiceManager |
+ */ |
+ public function createScopedServiceManager($peering = self::SCOPE_PARENT) |
+ { |
+ $scopedServiceManager = new ServiceManager(); |
+ if ($peering == self::SCOPE_PARENT) { |
+ $scopedServiceManager->peeringServiceManagers[] = $this; |
+ } |
+ if ($peering == self::SCOPE_CHILD) { |
+ $this->peeringServiceManagers[] = $scopedServiceManager; |
+ } |
+ return $scopedServiceManager; |
+ } |
+ |
+ /** |
+ * Add a peering relationship |
+ * |
+ * @param ServiceManager $manager |
+ * @param string $peering |
+ * @return ServiceManager |
+ */ |
+ public function addPeeringServiceManager(ServiceManager $manager, $peering = self::SCOPE_PARENT) |
+ { |
+ if ($peering == self::SCOPE_PARENT) { |
+ $this->peeringServiceManagers[] = $manager; |
+ } |
+ if ($peering == self::SCOPE_CHILD) { |
+ $manager->peeringServiceManagers[] = $this; |
+ } |
+ return $this; |
+ } |
+ |
+ /** |
+ * Canonicalize name |
+ * |
+ * @param string $name |
+ * @return string |
+ */ |
+ protected function canonicalizeName($name) |
+ { |
+ if (isset($this->canonicalNames[$name])) { |
+ return $this->canonicalNames[$name]; |
+ } |
+ |
+ // this is just for performance instead of using str_replace |
+ return $this->canonicalNames[$name] = strtolower(strtr($name, $this->canonicalNamesReplacements)); |
+ } |
+ |
+ /** |
+ * Create service via callback |
+ * |
+ * @param callable $callable |
+ * @param string $cName |
+ * @param string $rName |
+ * @throws Exception\ServiceNotCreatedException |
+ * @throws Exception\ServiceNotFoundException |
+ * @throws Exception\CircularDependencyFoundException |
+ * @return object |
+ */ |
+ protected function createServiceViaCallback($callable, $cName, $rName) |
+ { |
+ static $circularDependencyResolver = array(); |
+ $depKey = spl_object_hash($this) . '-' . $cName; |
+ |
+ if (isset($circularDependencyResolver[$depKey])) { |
+ $circularDependencyResolver = array(); |
+ throw new Exception\CircularDependencyFoundException('Circular dependency for LazyServiceLoader was found for instance ' . $rName); |
+ } |
+ |
+ try { |
+ $circularDependencyResolver[$depKey] = true; |
+ $instance = call_user_func($callable, $this, $cName, $rName); |
+ unset($circularDependencyResolver[$depKey]); |
+ } catch (Exception\ServiceNotFoundException $e) { |
+ unset($circularDependencyResolver[$depKey]); |
+ throw $e; |
+ } catch (\Exception $e) { |
+ unset($circularDependencyResolver[$depKey]); |
+ throw new Exception\ServiceNotCreatedException( |
+ sprintf('An exception was raised while creating "%s"; no instance returned', $rName), |
+ $e->getCode(), |
+ $e |
+ ); |
+ } |
+ if ($instance === null) { |
+ throw new Exception\ServiceNotCreatedException('The factory was called but did not return an instance.'); |
+ } |
+ |
+ return $instance; |
+ } |
+ |
+ /** |
+ * Retrieve a keyed list of all registered services. Handy for debugging! |
+ * |
+ * @return array |
+ */ |
+ public function getRegisteredServices() |
+ { |
+ return array( |
+ 'invokableClasses' => array_keys($this->invokableClasses), |
+ 'factories' => array_keys($this->factories), |
+ 'aliases' => array_keys($this->aliases), |
+ 'instances' => array_keys($this->instances), |
+ ); |
+ } |
+ |
+ /** |
+ * Retrieve a keyed list of all canonical names. Handy for debugging! |
+ * |
+ * @return array |
+ */ |
+ public function getCanonicalNames() |
+ { |
+ return $this->canonicalNames; |
+ } |
+ |
+ /** |
+ * Allows to override the canonical names lookup map with predefined |
+ * values. |
+ * |
+ * @param array $canonicalNames |
+ * @return ServiceManager |
+ */ |
+ public function setCanonicalNames($canonicalNames) |
+ { |
+ $this->canonicalNames = $canonicalNames; |
+ |
+ return $this; |
+ } |
+ |
+ /** |
+ * Attempt to retrieve an instance via a peering manager |
+ * |
+ * @param string $name |
+ * @return mixed |
+ */ |
+ protected function retrieveFromPeeringManager($name) |
+ { |
+ foreach ($this->peeringServiceManagers as $peeringServiceManager) { |
+ if ($peeringServiceManager->has($name)) { |
+ return $peeringServiceManager->get($name); |
+ } |
+ } |
+ |
+ $name = $this->canonicalizeName($name); |
+ |
+ if ($this->hasAlias($name)) { |
+ do { |
+ $name = $this->aliases[$name]; |
+ } while ($this->hasAlias($name)); |
+ } |
+ |
+ foreach ($this->peeringServiceManagers as $peeringServiceManager) { |
+ if ($peeringServiceManager->has($name)) { |
+ return $peeringServiceManager->get($name); |
+ } |
+ } |
+ |
+ return null; |
+ } |
+ |
+ /** |
+ * Attempt to create an instance via an invokable class |
+ * |
+ * @param string $canonicalName |
+ * @param string $requestedName |
+ * @return null|\stdClass |
+ * @throws Exception\ServiceNotFoundException If resolved class does not exist |
+ */ |
+ protected function createFromInvokable($canonicalName, $requestedName) |
+ { |
+ $invokable = $this->invokableClasses[$canonicalName]; |
+ if (!class_exists($invokable)) { |
+ throw new Exception\ServiceNotFoundException(sprintf( |
+ '%s: failed retrieving "%s%s" via invokable class "%s"; class does not exist', |
+ __METHOD__, |
+ $canonicalName, |
+ ($requestedName ? '(alias: ' . $requestedName . ')' : ''), |
+ $invokable |
+ )); |
+ } |
+ $instance = new $invokable; |
+ return $instance; |
+ } |
+ |
+ /** |
+ * Attempt to create an instance via a factory |
+ * |
+ * @param string $canonicalName |
+ * @param string $requestedName |
+ * @return mixed |
+ * @throws Exception\ServiceNotCreatedException If factory is not callable |
+ */ |
+ protected function createFromFactory($canonicalName, $requestedName) |
+ { |
+ $factory = $this->factories[$canonicalName]; |
+ if (is_string($factory) && class_exists($factory, true)) { |
+ $factory = new $factory; |
+ $this->factories[$canonicalName] = $factory; |
+ } |
+ if ($factory instanceof FactoryInterface) { |
+ $instance = $this->createServiceViaCallback(array($factory, 'createService'), $canonicalName, $requestedName); |
+ } elseif (is_callable($factory)) { |
+ $instance = $this->createServiceViaCallback($factory, $canonicalName, $requestedName); |
+ } else { |
+ throw new Exception\ServiceNotCreatedException(sprintf( |
+ 'While attempting to create %s%s an invalid factory was registered for this instance type.', |
+ $canonicalName, |
+ ($requestedName ? '(alias: ' . $requestedName . ')' : '') |
+ )); |
+ } |
+ return $instance; |
+ } |
+ |
+ /** |
+ * Attempt to create an instance via an abstract factory |
+ * |
+ * @param string $canonicalName |
+ * @param string $requestedName |
+ * @return object|null |
+ * @throws Exception\ServiceNotCreatedException If abstract factory is not callable |
+ */ |
+ protected function createFromAbstractFactory($canonicalName, $requestedName) |
+ { |
+ foreach ($this->abstractFactories as $index => $abstractFactory) { |
+ // support factories as strings |
+ if (is_string($abstractFactory) && class_exists($abstractFactory, true)) { |
+ $this->abstractFactories[$index] = $abstractFactory = new $abstractFactory; |
+ } elseif (!$abstractFactory instanceof AbstractFactoryInterface) { |
+ throw new Exception\ServiceNotCreatedException(sprintf( |
+ 'While attempting to create %s%s an abstract factory could not produce a valid instance.', |
+ $canonicalName, |
+ ($requestedName ? '(alias: ' . $requestedName . ')' : '') |
+ )); |
+ } |
+ try { |
+ if ($abstractFactory->canCreateServiceWithName($this, $canonicalName, $requestedName)) { |
+ $this->pendingAbstractFactoryRequests[get_class($abstractFactory)] = $requestedName; |
+ $instance = $this->createServiceViaCallback( |
+ array($abstractFactory, 'createServiceWithName'), |
+ $canonicalName, |
+ $requestedName |
+ ); |
+ unset($this->pendingAbstractFactoryRequests[get_class($abstractFactory)]); |
+ } else { |
+ $instance = false; |
+ } |
+ } catch (\Exception $e) { |
+ unset($this->pendingAbstractFactoryRequests[get_class($abstractFactory)]); |
+ throw new Exception\ServiceNotCreatedException( |
+ sprintf( |
+ 'An abstract factory could not create an instance of %s%s.', |
+ $canonicalName, |
+ ($requestedName ? '(alias: ' . $requestedName . ')' : '') |
+ ), |
+ $e->getCode(), |
+ $e |
+ ); |
+ } |
+ if (is_object($instance)) { |
+ break; |
+ } |
+ } |
+ |
+ return $instance; |
+ } |
+ |
+ /** |
+ * Checks if the object has this class as one of its parents |
+ * |
+ * @see https://bugs.php.net/bug.php?id=53727 |
+ * @see https://github.com/zendframework/zf2/pull/1807 |
+ * |
+ * @param string $className |
+ * @param string $type |
+ * @return bool |
+ */ |
+ protected static function isSubclassOf($className, $type) |
+ { |
+ if (is_subclass_of($className, $type)) { |
+ return true; |
+ } |
+ if (version_compare(PHP_VERSION, '5.3.7', '>=')) { |
+ return false; |
+ } |
+ if (!interface_exists($type)) { |
+ return false; |
+ } |
+ $r = new ReflectionClass($className); |
+ return $r->implementsInterface($type); |
+ } |
+ |
+ /** |
+ * Unregister a service |
+ * |
+ * Called when $allowOverride is true and we detect that a service being |
+ * added to the instance already exists. This will remove the duplicate |
+ * entry, and also any shared flags previously registered. |
+ * |
+ * @param string $canonical |
+ * @return void |
+ */ |
+ protected function unregisterService($canonical) |
+ { |
+ $types = array('invokableClasses', 'factories', 'aliases'); |
+ foreach ($types as $type) { |
+ if (isset($this->{$type}[$canonical])) { |
+ unset($this->{$type}[$canonical]); |
+ break; |
+ } |
+ } |
+ |
+ if (isset($this->instances[$canonical])) { |
+ unset($this->instances[$canonical]); |
+ } |
+ |
+ if (isset($this->shared[$canonical])) { |
+ unset($this->shared[$canonical]); |
+ } |
+ } |
+} |
Index: trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/AbstractPluginManager.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/AbstractPluginManager.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/AbstractPluginManager.php (revision 80) |
@@ -0,0 +1,214 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\ServiceManager; |
+ |
+/** |
+ * ServiceManager implementation for managing plugins |
+ * |
+ * Automatically registers an initializer which should be used to verify that |
+ * a plugin instance is of a valid type. Additionally, allows plugins to accept |
+ * an array of options for the constructor, which can be used to configure |
+ * the plugin when retrieved. Finally, enables the allowOverride property by |
+ * default to allow registering factories, aliases, and invokables to take |
+ * the place of those provided by the implementing class. |
+ */ |
+abstract class AbstractPluginManager extends ServiceManager implements ServiceLocatorAwareInterface |
+{ |
+ /** |
+ * Allow overriding by default |
+ * |
+ * @var bool |
+ */ |
+ protected $allowOverride = true; |
+ |
+ /** |
+ * Whether or not to auto-add a class as an invokable class if it exists |
+ * |
+ * @var bool |
+ */ |
+ protected $autoAddInvokableClass = true; |
+ |
+ /** |
+ * Options to use when creating an instance |
+ * |
+ * @var mixed |
+ */ |
+ protected $creationOptions = null; |
+ |
+ /** |
+ * The main service locator |
+ * |
+ * @var ServiceLocatorInterface |
+ */ |
+ protected $serviceLocator; |
+ |
+ /** |
+ * Constructor |
+ * |
+ * Add a default initializer to ensure the plugin is valid after instance |
+ * creation. |
+ * |
+ * @param null|ConfigInterface $configuration |
+ */ |
+ public function __construct(ConfigInterface $configuration = null) |
+ { |
+ parent::__construct($configuration); |
+ $self = $this; |
+ $this->addInitializer(function ($instance) use ($self) { |
+ if ($instance instanceof ServiceLocatorAwareInterface) { |
+ $instance->setServiceLocator($self); |
+ } |
+ }); |
+ } |
+ |
+ /** |
+ * Validate the plugin |
+ * |
+ * Checks that the filter loaded is either a valid callback or an instance |
+ * of FilterInterface. |
+ * |
+ * @param mixed $plugin |
+ * @return void |
+ * @throws Exception\RuntimeException if invalid |
+ */ |
+ abstract public function validatePlugin($plugin); |
+ |
+ /** |
+ * Retrieve a service from the manager by name |
+ * |
+ * Allows passing an array of options to use when creating the instance. |
+ * createFromInvokable() will use these and pass them to the instance |
+ * constructor if not null and a non-empty array. |
+ * |
+ * @param string $name |
+ * @param array $options |
+ * @param bool $usePeeringServiceManagers |
+ * @return object |
+ */ |
+ public function get($name, $options = array(), $usePeeringServiceManagers = true) |
+ { |
+ // Allow specifying a class name directly; registers as an invokable class |
+ if (!$this->has($name) && $this->autoAddInvokableClass && class_exists($name)) { |
+ $this->setInvokableClass($name, $name); |
+ } |
+ |
+ $this->creationOptions = $options; |
+ $instance = parent::get($name, $usePeeringServiceManagers); |
+ $this->creationOptions = null; |
+ $this->validatePlugin($instance); |
+ return $instance; |
+ } |
+ |
+ /** |
+ * Register a service with the locator. |
+ * |
+ * Validates that the service object via validatePlugin() prior to |
+ * attempting to register it. |
+ * |
+ * @param string $name |
+ * @param mixed $service |
+ * @param bool $shared |
+ * @return AbstractPluginManager |
+ * @throws Exception\InvalidServiceNameException |
+ */ |
+ public function setService($name, $service, $shared = true) |
+ { |
+ if ($service) { |
+ $this->validatePlugin($service); |
+ } |
+ parent::setService($name, $service, $shared); |
+ return $this; |
+ } |
+ |
+ /** |
+ * Set the main service locator so factories can have access to it to pull deps |
+ * |
+ * @param ServiceLocatorInterface $serviceLocator |
+ * @return AbstractPluginManager |
+ */ |
+ public function setServiceLocator(ServiceLocatorInterface $serviceLocator) |
+ { |
+ $this->serviceLocator = $serviceLocator; |
+ return $this; |
+ } |
+ |
+ /** |
+ * Get the main plugin manager. Useful for fetching dependencies from within factories. |
+ * |
+ * @return mixed |
+ */ |
+ public function getServiceLocator() |
+ { |
+ return $this->serviceLocator; |
+ } |
+ |
+ /** |
+ * Attempt to create an instance via an invokable class |
+ * |
+ * Overrides parent implementation by passing $creationOptions to the |
+ * constructor, if non-null. |
+ * |
+ * @param string $canonicalName |
+ * @param string $requestedName |
+ * @return null|\stdClass |
+ * @throws Exception\ServiceNotCreatedException If resolved class does not exist |
+ */ |
+ protected function createFromInvokable($canonicalName, $requestedName) |
+ { |
+ $invokable = $this->invokableClasses[$canonicalName]; |
+ |
+ if (null === $this->creationOptions |
+ || (is_array($this->creationOptions) && empty($this->creationOptions)) |
+ ) { |
+ $instance = new $invokable(); |
+ } else { |
+ $instance = new $invokable($this->creationOptions); |
+ } |
+ |
+ return $instance; |
+ } |
+ |
+ /** |
+ * Attempt to create an instance via a factory class |
+ * |
+ * Overrides parent implementation by passing $creationOptions to the |
+ * constructor, if non-null. |
+ * |
+ * @param string $canonicalName |
+ * @param string $requestedName |
+ * @return mixed |
+ * @throws Exception\ServiceNotCreatedException If factory is not callable |
+ */ |
+ protected function createFromFactory($canonicalName, $requestedName) |
+ { |
+ $factory = $this->factories[$canonicalName]; |
+ if (is_string($factory) && class_exists($factory, true)) { |
+ if (null === $this->creationOptions || (is_array($this->creationOptions) && empty($this->creationOptions))) { |
+ $factory = new $factory(); |
+ } else { |
+ $factory = new $factory($this->creationOptions); |
+ } |
+ |
+ $this->factories[$canonicalName] = $factory; |
+ } |
+ |
+ if ($factory instanceof FactoryInterface) { |
+ $instance = $this->createServiceViaCallback(array($factory, 'createService'), $canonicalName, $requestedName); |
+ } elseif (is_callable($factory)) { |
+ $instance = $this->createServiceViaCallback($factory, $canonicalName, $requestedName); |
+ } else { |
+ throw new Exception\ServiceNotCreatedException(sprintf( |
+ 'While attempting to create %s%s an invalid factory was registered for this instance type.', $canonicalName, ($requestedName ? '(alias: ' . $requestedName . ')' : '') |
+ )); |
+ } |
+ |
+ return $instance; |
+ } |
+} |
Index: trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceLocatorAwareInterface.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceLocatorAwareInterface.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceLocatorAwareInterface.php (revision 80) |
@@ -0,0 +1,27 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\ServiceManager; |
+ |
+interface ServiceLocatorAwareInterface |
+{ |
+ /** |
+ * Set service locator |
+ * |
+ * @param ServiceLocatorInterface $serviceLocator |
+ */ |
+ public function setServiceLocator(ServiceLocatorInterface $serviceLocator); |
+ |
+ /** |
+ * Get service locator |
+ * |
+ * @return ServiceLocatorInterface |
+ */ |
+ public function getServiceLocator(); |
+} |
Index: trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceLocatorInterface.php |
=================================================================== |
--- trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceLocatorInterface.php (nonexistent) |
+++ trunk/ZendFramework-2.1.2/library/Zend/ServiceManager/ServiceLocatorInterface.php (revision 80) |
@@ -0,0 +1,33 @@ |
+<?php |
+/** |
+ * Zend Framework (http://framework.zend.com/) |
+ * |
+ * @link http://github.com/zendframework/zf2 for the canonical source repository |
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) |
+ * @license http://framework.zend.com/license/new-bsd New BSD License |
+ */ |
+ |
+namespace Zend\ServiceManager; |
+ |
+/** |
+ * Service locator interface |
+ */ |
+interface ServiceLocatorInterface |
+{ |
+ /** |
+ * Retrieve a registered instance |
+ * |
+ * @param string $name |
+ * @throws Exception\ServiceNotFoundException |
+ * @return object|array |
+ */ |
+ public function get($name); |
+ |
+ /** |
+ * Check for a registered instance |
+ * |
+ * @param string|array $name |
+ * @return bool |
+ */ |
+ public function has($name); |
+} |
Index: trunk/css/least/Mixins.php |
=================================================================== |
--- trunk/css/least/Mixins.php (nonexistent) |
+++ trunk/css/least/Mixins.php (revision 80) |
@@ -0,0 +1,180 @@ |
+<?php |
+ |
+namespace de\pointedears\css\least; |
+ |
+/** |
+ * Mix-ins for the LEAST CSS preprocessor |
+ * |
+ * @author Thomas 'PointedEars' Lahn <php@PointedEars.de> |
+ */ |
+abstract class Mixins |
+{ |
+ /** @section General functions */ |
+ |
+ /** |
+ * Generates a CSS section for each keyword prefix. |
+ * |
+ * @param string $keyword |
+ * Section keyword |
+ * @param string $params |
+ * Section parameters (name, medium, etc.) |
+ * @param string $content |
+ * Section content |
+ * @param array[string] $prefixes |
+ * Keyword prefixes |
+ */ |
+ public static function prefix_section ($keyword, $params, |
+ $content, array $prefixes) |
+ { |
+ ob_start(); |
+ foreach ($prefixes as $prefix) |
+ { |
+ echo "@{$prefix}{$keyword} {$params} {\n {$content}\n}\n"; |
+ } |
+ ob_end_flush(); |
+ } |
+ |
+ /** |
+ * Generates a CSS property declaration whose value is a |
+ * function call, for each function name prefix. |
+ * |
+ * @param string $property |
+ * Property to be declared |
+ * @param string $function |
+ * CSS function to be called |
+ * @param string $args |
+ * Arguments to the function |
+ * @param array[string] $prefixes |
+ * Function name prefixes |
+ */ |
+ public static function prefix_function ($property, $function, $args, |
+ array $prefixes) |
+ { |
+ ob_start(); |
+ foreach ($prefixes as $prefix) |
+ { |
+ echo "{$property}: {$prefix}{$function}({$args});\n"; |
+ } |
+ ob_end_flush(); |
+ } |
+ |
+ /** |
+ * Generates a CSS property declaration for each |
+ * property name prefix. |
+ * |
+ * @param string $suffix |
+ * Property name suffix |
+ * @param string $value |
+ * Property value |
+ * @param array[string] $prefixes |
+ * Property name prefixes |
+ */ |
+ public static function prefix_property ($property, $suffix, $value, |
+ array $prefixes) |
+ { |
+ ob_start(); |
+ foreach ($prefixes as $prefix) |
+ { |
+ echo "{$prefix}{$property}{$suffix}: {$value};\n"; |
+ } |
+ ob_end_flush(); |
+ } |
+ |
+ /** @section Gradients */ |
+ |
+ /** |
+ * Generates a CSS property declaration whose value is a |
+ * <code>linear-gradient()</code> function call for |
+ * each function name prefix. |
+ * |
+ * @param string $property |
+ * Property to be declared |
+ * @param string $args |
+ * Arguments to the <code>linear-gradient()</code> function |
+ * @param array[string] $prefixes (optional) |
+ * Pass to override supported function name prefixes |
+ * @see self::prefix_function() |
+ */ |
+ public static function linear_gradient ($property, $args, |
+ array $prefixes = array('-moz-', '-o-', '-webkit-', '')) |
+ { |
+ self::prefix_function($property, 'linear-gradient', $args, $prefixes); |
+ } |
+ |
+ /** |
+ * Generates a CSS property declaration whose value is a |
+ * <code>radial-gradient()</code> function call for each |
+ * function name prefix. |
+ * |
+ * @param string $property |
+ * Property to be declared |
+ * @param string $args |
+ * Arguments to the <code>radial-gradient()</code> function |
+ * @param array[string] $prefixes (optional) |
+ * Pass to override supported function name prefixes |
+ * @see self::prefix_function() |
+ */ |
+ public static function radial_gradient ($property, $args, |
+ array $prefixes = array('-moz-', '-webkit-')) |
+ { |
+ self::prefix_function($property, 'radial-gradient', $args, $prefixes); |
+ } |
+ |
+ /** |
+ * Generates a CSS <code>transition</code> property declaration |
+ * for each property name prefix. |
+ * |
+ * @param string $suffix |
+ * Property name suffix |
+ * @param string $value |
+ * Property value |
+ * @param array[string] $prefixes (optional) |
+ * Pass to override supported property name prefixes |
+ * @see self::prefix_property() |
+ */ |
+ public static function transition ($suffix, $value, |
+ array $prefixes = array('-moz-', '-webkit-', '')) |
+ { |
+ self::prefix_property('transition', $suffix, $value, $prefixes); |
+ } |
+ |
+ /** @section Animations */ |
+ |
+ /** |
+ * Generates a CSS <code>@keyframes</code> section for |
+ * each keyword prefix. |
+ * |
+ * @param string $name |
+ * Animation name as referred by an <code>animation-name</code> |
+ * property value. |
+ * @param string $data |
+ * Keyframes data |
+ * @param array[string] $prefixes (optional) |
+ * Pass to override supported keyword prefixes |
+ * @see self::prefix_section() |
+ */ |
+ public static function keyframes ($name, $data, |
+ array $prefixes = array('-moz-', '-webkit-', '')) |
+ { |
+ self::prefix_section('keyframes', $name, $data, $prefixes); |
+ } |
+ |
+ /** |
+ * Generates a CSS <code>animation</code> property declaration |
+ * for each property name prefix. |
+ * |
+ * @param string $suffix |
+ * Property name suffix, e.g. <tt>"-name"</tt> for |
+ * <code>animation-name</code> |
+ * @param string $value |
+ * Property value |
+ * @param array[string] $prefixes (optional) |
+ * Pass to override supported property name prefixes |
+ * @see self::prefix_property() |
+ */ |
+ public static function animation ($suffix, $value, |
+ array $prefixes = array('-moz-', '-webkit-', '')) |
+ { |
+ self::prefix_property('animation', $suffix, $value, $prefixes); |
+ } |
+} |
\ No newline at end of file |
Index: trunk/css/least/Parser.php |
=================================================================== |
--- trunk/css/least/Parser.php (nonexistent) |
+++ trunk/css/least/Parser.php (revision 80) |
@@ -0,0 +1,345 @@ |
+<?php |
+ |
+namespace de\pointedears\css\least; |
+ |
+require_once __DIR__ . '/../../string/parser/Lexer.php'; |
+require_once __DIR__ . '/../../string/parser/Parser.php'; |
+ |
+/** |
+ * Parses a LEAST stylesheet into a CSS stylesheet |
+ * |
+ * @author Thomas 'PointedEars' Lahn |
+ * @property-read string $compiled |
+ */ |
+class Parser extends \de\pointedears\string\parser\Parser |
+{ |
+ /** |
+ * CSS stylesheet code compiled from the LEAST stylesheet |
+ * |
+ * @var string |
+ */ |
+ protected $_compiled; |
+ |
+ /** |
+ * Array of arrays of template variables, one inner array for |
+ * each scope level. |
+ * @var array[array] |
+ */ |
+ protected $_vars = array(array()); |
+ |
+ /** |
+ * The nesting level of the current variable scope. |
+ * |
+ * Used to hold variable definitions per nesting level. |
+ * |
+ * @var int |
+ */ |
+ protected $_scope_level = 0; |
+ |
+ /** |
+ * Cache for lookup results. |
+ * |
+ * The lookup cache improves parser performance with |
+ * nested scopes. By contrast to <code>$_vars</code>, it is |
+ * a _shallow_ storage for template variable definitions and |
+ * represents the current _certain_ variable knowledge of the |
+ * parser, i.e. variables that have been defined in outer scopes |
+ * and the same scope, and whose values have been looked up in |
+ * <code>$_vars</code> before. |
+ * |
+ * The lookup cache is replaced by the variables of the outer |
+ * scope when a scope is exited because definitions in the |
+ * outer scope may have been shadowed by the inner scope. |
+ * Note that definitions from the second-next, third-next |
+ * aso. scopes need to be looked up again then. |
+ * |
+ * @var array |
+ */ |
+ protected $_lookup_cache = array(); |
+ |
+ /** |
+ * Mixins |
+ * @var array |
+ */ |
+ protected $_mixins = array(); |
+ |
+ /** |
+ * <code>true</code> if the parser is parsing a mix-in |
+ * @var boolean |
+ */ |
+ protected $_in_mixin = false; |
+ |
+ /** |
+ * The currently parsed mix-in |
+ * @var Mixin |
+ */ |
+ protected $_current_mixin; |
+ |
+ /** |
+ * The nesting level in the currently parsed mix-in, 0 again when |
+ * it just ended |
+ * @var int |
+ */ |
+ protected $_mixin_level = 0; |
+ |
+ public function __construct ($code) |
+ { |
+ $lexer = new \de\pointedears\string\parser\Lexer($code); |
+ |
+ $nl = 'n'; |
+ $escape = '\\[0-9a-f]{1,6}(\r\n|[ \t\r\n\f])?|\\[^\r\n\f0-9a-f]'; |
+ $lexer->addTokens(array( |
+ '(?P<PHP><\?php.*?\?>)', |
+ "/\*[^*]*\*+([^/*][^*]*\*+)*/", |
+ "\"([^\n\r\f\\\"]|\\{$nl}|{$escape})*\"", |
+ "'([^\n\r\f\\']|\\{$nl}|{$escape})*'", |
+// '\.(?P<mixin_def>[\w-]+)\s*\((?P<params>.*?)\)\s*(?!;)', |
+// '\.(?P<mixin_call>[\w-]+)\s*\((?P<args>.*)?\)\s*;\\s*', |
+ '[{}]', |
+// '\$(?P<name>\w+)\s*=\s*(?P<value>\'.+?\'|".+"|.+?)\s*;\s*', |
+// '\$(?P<ref>\w+)' |
+ )); |
+ |
+ $lexer->ignoreCase = true; |
+ $lexer->dotAll = true; |
+ |
+ parent::__construct($lexer); |
+ $this->_compiled = $code; |
+ } |
+ |
+ protected function defineVar($name, $value) |
+ { |
+ $scope_level = $this->_scope_level; |
+ |
+ if (defined('DEBUG') && DEBUG > 0) |
+ { |
+ echo "<?php // Least.php: Found definition \${$name} = `{$value}'" |
+ . " at scope level {$scope_level} ?>\n"; |
+ } |
+ |
+ $this->_vars[$scope_level][$name] = $value; |
+ $this->_lookup_cache[$name] = $value; |
+ } |
+ |
+ /** |
+ * Resolves a template variable reference against the template scope chain |
+ * @param string $ref |
+ * @return mixed |
+ */ |
+ protected function resolveVar ($name) |
+ { |
+ $scope_level = $this->_scope_level + 1; |
+ $orig_level = $scope_level - 1; |
+ |
+ if (defined('DEBUG') && DEBUG > 0) |
+ { |
+ echo "<?php // Least.php: Found reference \${$name} at scope level " |
+ . ($scope_level - 1) . " ?>\n"; |
+ } |
+ |
+ /* Try the lookup cache for this scope level first */ |
+ if (array_key_exists($name, $this->_lookup_cache)) |
+ { |
+ $value = $this->_lookup_cache[$name]; |
+ |
+ if (defined('DEBUG') && DEBUG > 1) |
+ { |
+ echo "<?php // Least.php: Resolved reference \${$name}" |
+ . " at scope level {$orig_level}" |
+ . " from lookup cache, value `{$value}' ?>\n"; |
+ } |
+ |
+ return $value; |
+ } |
+ |
+ while ($scope_level--) |
+ { |
+ if (array_key_exists($name, $this->_vars[$scope_level])) |
+ { |
+ $value = $this->_vars[$scope_level][$name]; |
+ $this->_lookup_cache[$name] = $value; |
+ |
+ if (defined('DEBUG') && DEBUG > 1) |
+ { |
+ echo "<?php // Least.php: Resolved reference \${$name}" |
+ . " from scope level {$orig_level}" |
+ . " at scope level {$scope_level}, value `{$value}' ?>\n"; |
+ } |
+ |
+ return $value; |
+ } |
+ } |
+ |
+ if (defined('DEBUG') && DEBUG > 0) |
+ { |
+ echo "<?php // Least.php: WARNING: Unresolved reference \${$name}" |
+ . " at scope level {$orig_level}. Variable not in scope? ?>\n"; |
+ } |
+ } |
+ |
+ protected function defineMixin($name, $params) |
+ { |
+ $scope_level = $this->_scope_level; |
+ |
+ if (defined('DEBUG') && DEBUG > 0) |
+ { |
+ echo "<?php // Least.php: Found mixin definition {$name}($params)" |
+ . " at scope level {$scope_level}. ?>\n"; |
+ } |
+ |
+ $mixin = new Mixin($params); |
+ $this->_mixins[$name] = $mixin; |
+ |
+ return $mixin; |
+ } |
+ |
+ protected function callMixin($name, $args) |
+ { |
+ return $this->$_mixins[$name]->apply($args); |
+ } |
+ |
+ public function parseToken ($matches) |
+ { |
+ ini_set('html_errors', 0); |
+// var_dump($matches); |
+// /* Get match offset and match length */ |
+// $match = $matches[0]; |
+// $match_start = $match[1]; |
+// $match_length = mb_strlen($match[0]); |
+ |
+// /* Transform $matches to the format it is usually set as (without PREG_OFFSET_CAPTURE set) */ |
+// $matches = array_map(function ($m) { |
+// return $m[0]; |
+// }, $matches); |
+// $match = $matches[0]; |
+ |
+// if (defined('DEBUG') && DEBUG > 1) |
+// { |
+// echo print_r(array( |
+// 'match_start' => $match_start, |
+// 'match_length' => $match_length, |
+// 'matches' => $matches, |
+// 'in_mixin' => $in_mixin, |
+// 'scope_level' => $this->_scope_level, |
+// ), true) . "\n"; |
+// } |
+ |
+// if (isset($matches['mixin_def']) && $matches['mixin_def']) |
+// { |
+// $this->_in_mixin = true; |
+// $this->_current_mixin = $this->defineMixin($matches['mixin_def'], $matches['params']); |
+// // $code = self::replace(mb_strlen($match), '', $code, $match_start); |
+// // $match_length = 0; |
+// } |
+// else if (isset($matches['mixin_call']) && $matches['mixin_call']) |
+// { |
+// //return |
+// $this->callMixin($matches['mixin_call'], $matches['args']); |
+// //$code = self::replace(mb_strlen($match), '', $code, $match_start); |
+// //$match_length = 0; |
+// $mixin_start = $match_start; |
+// } |
+// else if ($match === '{') |
+// { |
+// ++$this->_scope_level; |
+// $this->_vars[$this->_scope_level] = array(); |
+ |
+// if ($this->_in_mixin) |
+// { |
+// if ($mixin_body_start === 0) |
+// { |
+// $mixin_body_start = $match_start; |
+// } |
+ |
+// ++$this->_mixin_level; |
+// } |
+// } |
+// else if ($match === '}') |
+// { |
+// --$this->_scope_level; |
+// $this->_lookup_cache = $this->_vars[$this->_scope_level]; |
+ |
+// if ($this->_in_mixin) |
+// { |
+// --$this->_mixin_level; |
+// if ($this->_mixin_level === 0) |
+// { |
+// $this->_in_mixin = false; |
+// $this->_current_mixin->setBody(substr($code, $mixin_start + 1, $match_start - $mixin_start - 1)); |
+// echo '"'.print_r(substr($code, $mixin_start, $match_start - $mixin_start + 1), true) . "\"\n"; |
+// // $code = self::replace($match_start - $mixin_start + 1, '', $code, $mixin_start); |
+// // $match_length = 0; |
+// } |
+// } |
+// } |
+// else if (isset($matches['name']) && $matches['name']) |
+// { |
+// $this->defineVar($matches['name'], $matches['value']); |
+// $code = self::replace(mb_strlen($match), '', $code, $match_start); |
+// $match_length = 0; |
+// } |
+// else if (isset($matches['ref']) && $matches['ref']) |
+// { |
+// $name = $matches['ref']; |
+ |
+// if (!$in_mixin || !in_array($name, $mixin->params, true)) |
+// { |
+// $value = $this->resolveVar($matches['ref']); |
+// $this->_lexer->text = self::replace(mb_strlen($match), $value, $code, $match_start); |
+// $this->_lexer->offset -= $match_length + mb_strlen($value); |
+// } |
+// } |
+ } |
+ |
+ public function getCompiled () |
+ { |
+ return $this->_compiled; |
+ } |
+} |
+ |
+class Mixin extends \de\pointedears\Base |
+{ |
+ /** |
+ * Parameters of the mixin |
+ * @var array |
+ */ |
+ protected $_params; |
+ |
+ /** |
+ * Body of the mixin |
+ * @var string |
+ */ |
+ protected $_body = ''; |
+ |
+ /** |
+ * @param string $params |
+ */ |
+ public function __construct ($params, $body = '') |
+ { |
+ $this->_params = is_array($params) |
+ ? $params |
+ : array_map( |
+ function ($e) { |
+ return preg_replace('/^\$/', '', $e); |
+ }, |
+ preg_split('/\s*,\s*/', $params)); |
+ |
+ if ($body !== '') |
+ { |
+ $this->setBody($body); |
+ } |
+ } |
+ |
+ public function setBody ($body) |
+ { |
+ $this->body = (string) $body; |
+ } |
+ |
+ /** |
+ * @param string $arguments |
+ */ |
+ public function apply ($arguments) |
+ { |
+ return ''; |
+ } |
+} |
\ No newline at end of file |
Index: trunk/css/least/least-jit.php |
=================================================================== |
--- trunk/css/least/least-jit.php (nonexistent) |
+++ trunk/css/least/least-jit.php (revision 80) |
@@ -0,0 +1,17 @@ |
+<?php |
+ |
+$file = $_GET['src']; |
+ |
+header('Last-Modified: ' . gmdate('D, d M Y H:i:s', @filemtime($file)) . ' GMT'); |
+ |
+/* Cached resource expires in HTTP/1.1 caches immediately after last retrieval */ |
+header('Cache-Control: max-age=0, s-maxage=0, must-revalidate, proxy-revalidate'); |
+ |
+/* Cached resource expires in HTTP/1.0 caches immediately after last retrieval */ |
+header('Expires: ' . gmdate('D, d M Y H:i:s', time() /*+ 86400*/) . ' GMT'); |
+ |
+header('Content-Type: text/plain; charset=UTF-8'); |
+ |
+require __DIR__ . '/LEAST.php'; |
+$least = new de\pointedears\css\least\LEAST(); |
+/*echo*/ $least->parse_file($file); |
\ No newline at end of file |
Index: trunk/css/least/LEAST.php |
=================================================================== |
--- trunk/css/least/LEAST.php (nonexistent) |
+++ trunk/css/least/LEAST.php (revision 80) |
@@ -0,0 +1,138 @@ |
+<?php |
+ |
+namespace de\pointedears\css\least; |
+ |
+require_once 'Base.php'; |
+ |
+require_once 'Parser.php'; |
+ |
+define('DEBUG', 2); |
+ |
+/** |
+ * @author Thomas 'PointedEars' Lahn |
+ */ |
+class LEAST |
+{ |
+ /** |
+ * Default name of parsed file |
+ * |
+ * @var string |
+ */ |
+ protected $_filename; |
+ |
+ /** |
+ * Default name of parsed file |
+ * |
+ * @param string $filename |
+ */ |
+ public function __construct ($code = null) |
+ { |
+ if ($code !== null) |
+ { |
+ $this->_code = $code; |
+ } |
+ } |
+ |
+ /** |
+ * Gets a template variable or a property |
+ * |
+ * @param string $name |
+ * @throws InvalidArgumentException |
+ * @return mixed |
+ */ |
+ public function __get($name) |
+ { |
+ if (property_exists($this, "_$name")) |
+ { |
+ return $this->{"_$name"}; |
+ } |
+ |
+ if (array_key_exists($name, $this->_vars)) |
+ { |
+ return $this->_vars[$name]; |
+ } |
+ |
+ throw new InvalidArgumentException("no property '{$name}'"); |
+ } |
+ |
+ /** |
+ * Sets a template variable |
+ * |
+ * @param string $name |
+ * @param mixed $value |
+ */ |
+ public function __set($name, $value) |
+ { |
+ $this->_vars[$name] = $value; |
+ } |
+ |
+ /** |
+ * Replaces characters in LEAST source code |
+ * |
+ * @param string $count |
+ * Number of characters to replace |
+ * @param string $replacement |
+ * Replacement string |
+ * @param string $haystack |
+ * String to be searched |
+ * @param int $start |
+ * Start replace from here |
+ * @return string |
+ */ |
+ protected static function replace ($count, $replacement, $haystack, $start) |
+ { |
+ return substr($haystack, 0, $start) . $replacement . substr($haystack, $start + $count); |
+ } |
+ |
+ /** |
+ * Parses LEAST source code, replacing special tokens, and returns the result |
+ * |
+ * @param string[optional] $code |
+ * @return string |
+ */ |
+ public function parse ($code = null) |
+ { |
+ if ($code === null) |
+ { |
+ $code = $this->_code; |
+ } |
+ |
+ static $nl = 'n'; |
+ |
+ \mb_internal_encoding(mb_detect_encoding($code)); |
+ $parser = new \de\pointedears\css\least\Parser($code); |
+ $parser->parse(); |
+ |
+ return $parser->compiled; |
+// return $code; |
+ } |
+ |
+ /** |
+ * Parses a LEAST file and returns the result |
+ * |
+ * @param string $filename |
+ * @return string |
+ */ |
+ public function parse_file ($filename = null) |
+ { |
+ $contents = file_get_contents( |
+ $filename === null ? $this->filename : $filename); |
+ return $this->parse($contents); |
+ } |
+ |
+ /** |
+ * Compiles a CSS file from a LEAST file |
+ * |
+ * @param string $source |
+ * @param string $target |
+ */ |
+ public static function compile ($source, $target) |
+ { |
+ if (@filemtime($target) < @filemtime($source)) |
+ { |
+ $least = new self(); |
+ file_put_contents($target, $least->parse_file($source)); |
+ } |
+ } |
+} |
+ |
Index: trunk/features.class.php |
=================================================================== |
--- trunk/features.class.php (nonexistent) |
+++ trunk/features.class.php (revision 80) |
@@ -0,0 +1,571 @@ |
+<?php |
+ |
+require_once __DIR__ . '/global.inc'; |
+ |
+$encoding = mb_detect_encoding(file_get_contents($_SERVER['SCRIPT_FILENAME'])); |
+if ($encoding === 'ASCII') $encoding = 'iso-8859-1'; |
+define('FEATURES_ENCODING', $encoding); |
+ |
+/** |
+ * A list of language features with URNs definitions |
+ * for reference links. |
+ */ |
+class FeatureList |
+{ |
+ public $versions = array(); |
+ |
+ /** |
+ * Versions of implementations that are considered safe. |
+ * A feature is considered safe if it does not require |
+ * an implementation version above these versions. |
+ * |
+ * @var Array[string=>string] |
+ */ |
+ public $safeVersions = array(); |
+ |
+ /** |
+ * URNs that can be used for reference links. |
+ * |
+ * @var Array[string=>string] |
+ */ |
+ protected $urns = array(); |
+ |
+ /** |
+ * <code>true</code> generates form controls for submitting test case |
+ * results |
+ * |
+ * @var bool |
+ */ |
+ protected $testcase = false; |
+ |
+ /** |
+ * The list of language features |
+ * |
+ * @var array[Features] |
+ */ |
+ protected $items = array(); |
+ |
+ /** |
+ * Determines the number of printed items the table headings should be repeated |
+ * |
+ * @var int |
+ */ |
+ protected $headerRepeat = 25; |
+ |
+ /** |
+ * Initializes the FeatureList object |
+ * |
+ * @param array|Object $a |
+ * @return FeatureList |
+ */ |
+ public function __construct($a) |
+ { |
+ $aVars = get_class_vars(get_class($this)); |
+ |
+ |
+ foreach ($aVars as $key => $value) |
+ { |
+ if (isset($a[$key])) |
+ { |
+ $this->$key = $a[$key]; |
+ } |
+ } |
+ |
+ /* Inform items of ourself so that URNs can be used for links */ |
+ if (is_array($this->items)) |
+ { |
+ foreach ($this->items as &$item) |
+ { |
+ $item->setList($this); |
+ } |
+ } |
+ |
+ /* resolve URN references that are URNs */ |
+ if (is_array($this->urns)) |
+ { |
+ foreach ($this->urns as &$urn) |
+ { |
+ if (($url = $this->resolveURN($urn))) |
+ { |
+ $urn = $url; |
+ } |
+ } |
+ } |
+ } |
+ |
+ /* |
+ * Protected properties may be read, but not written |
+ */ |
+ public function __get($property) |
+ { |
+ if (property_exists(get_class($this), $property)) |
+ { |
+ return $this->$property; |
+ } |
+ } |
+ |
+ public function printHeaders() |
+ { |
+ foreach ($this->versions as $key => $ver) |
+ { |
+ if ($key || $this->testcase) |
+ { |
+?> |
+ <th><?php echo $ver; ?></th> |
+<?php |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Prints the list of features. |
+ * |
+ * @see Feature::printMe() |
+ */ |
+ public function printItems() |
+ { |
+ $counter = 0; |
+ $headerRepeat = $this->headerRepeat; |
+ $repeatHeaders = ($headerRepeat > 1); |
+ |
+ foreach ($this->items as $feature) |
+ { |
+ if ($feature instanceof Feature) |
+ { |
+ /* |
+ * TODO: Disabled header repetition until footnote ref. name/ID |
+ * problem has been solved |
+ */ |
+// if ($repeatHeaders |
+// && $counter > 1 |
+// && $counter % $headerRepeat === 0) |
+// { |
+// echo <<<HTML |
+// <tr class="header"> |
+// <th>Feature</th> |
+// {$this->printHeaders()} |
+// </tr> |
+//HTML; |
+// } |
+ |
+ $feature->printMe(); |
+ |
+ $counter++; |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Resolves a URN according to the value of the |
+ * object's <code>$urn</code> property. |
+ * |
+ * @param string $urn |
+ * URN to be resolved |
+ * @return string|boolean |
+ * The resolved URN if successful, |
+ * <code>false</code> otherwise. |
+ */ |
+ public function resolveURN($urn) |
+ { |
+ if (is_array($this->urns)) |
+ { |
+ $reURN = '|^(.+?):(?!//)|'; |
+ |
+ if (preg_match($reURN, $urn, $m) && isset($this->urns[$m[1]])) |
+ { |
+ return preg_replace($reURN, $this->urns[$m[1]], $urn); |
+ } |
+ } |
+ |
+ return $urn; |
+ } |
+} |
+ |
+/** |
+ * A language feature. |
+ * |
+ * @property-read Array[String] $anchors |
+ * Fragment identifiers to be defined for quickly accessing |
+ * the feature description. |
+ * @property-read string $title |
+ * Value of the explanatory <code>title</code> attribute for the feature. |
+ * @property-read string $content |
+ * Name or example code of the feature |
+ * @property-read string $descr |
+ * Description of the feature. Displayed directly if code is missing, |
+ * otherwise used as `title' attribute value. |
+ * @property-read Array $versions |
+ * Versions that support this feature |
+ * @property-read List $list |
+ * Reference to the FeatureList that this feature belongs to |
+ */ |
+class Feature |
+{ |
+ /** |
+ * Fragment identifiers to be defined for quickly accessing |
+ * the feature description. |
+ * |
+ * @var Array[String] |
+ */ |
+ protected $anchors = array(); |
+ |
+ /** |
+ * Value of the explanatory <code>title</code> attribute for the feature. |
+ * |
+ * @var string |
+ */ |
+ protected $title = ''; |
+ |
+ /** |
+ * Name or example code of the feature |
+ * |
+ * @var string |
+ */ |
+ protected $content = ''; |
+ |
+ /** |
+ * Description of the feature. Displayed directly if code is missing, |
+ * otherwise used as `title' attribute value. |
+ * |
+ * @var string |
+ */ |
+ protected $descr = ''; |
+ |
+ /** |
+ * Versions that support this feature |
+ * |
+ * @var Array |
+ */ |
+ protected $versions = array(); |
+ |
+ /** |
+ * Reference to the FeatureList that this feature belongs to |
+ * |
+ * @var FeatureList |
+ */ |
+ protected $list = null; |
+ |
+ public function setList(&$oList) |
+ { |
+ $this->list =& $oList; |
+ } |
+ |
+ /** |
+ * Creates a new Feature object, using values from the passed parameters |
+ * array. |
+ * |
+ * @param array|Object $params |
+ * @return Feature |
+ */ |
+ public function __construct($params = array()) |
+ { |
+ $aVars = get_class_vars(__CLASS__); |
+ |
+ foreach ($aVars as $key => $value) |
+ { |
+ if (isset($params[$key])) |
+ { |
+ $this->$key = $params[$key]; |
+ } |
+ } |
+ } |
+ |
+ /* |
+ * Protected properties may be read, but not written |
+ */ |
+ public function __get($property) |
+ { |
+ if (property_exists(get_class($this), $property)) |
+ { |
+ return $this->$property; |
+ } |
+ } |
+ |
+ /** |
+ * Determines whether one version is greater than another. |
+ * |
+ * @param string $v1 Version string #1 |
+ * @param string $v2 Version string #2 |
+ * @return bool |
+ * <code>true</code> if the version <var>$v1</var> is greater than |
+ * the version <var>$v2</var>, <code>false</code> otherwise |
+ */ |
+ protected static function _versionIsGreater($v1, $v2) |
+ { |
+ $v1 = explode('.', $v1); |
+ $v2 = explode('.', $v2); |
+ |
+ foreach ($v1 as $key => $value) |
+ { |
+ if ((int)$value <= (int)$v2[$key]) |
+ { |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
+ } |
+ |
+ /** |
+ * Returns <code>' class="safe"'</code> if the feature |
+ * can be considered safe. The required information |
+ * is stored in the <code>safeVersions</code> property |
+ * of the associated <code>FeatureList</code> object. |
+ * |
+ * @return string |
+ * @see FeatureList::defaultSafeVersions |
+ */ |
+ protected function getSafeStr() |
+ { |
+ if (!is_null($this->list)) |
+ { |
+ foreach ($this->list->safeVersions as $impl => &$safeVer) |
+ { |
+ $thisImplVer =& $this->versions[$impl]; |
+ if (is_array($thisImplVer)) |
+ { |
+ if (isset($thisImplVer['tested']) && !is_bool($thisImplVer['tested'])) |
+ { |
+ $thisImplVer =& $thisImplVer['tested']; |
+ } |
+ else |
+ { |
+ $thisImplVer =& $thisImplVer[0]; |
+ } |
+ } |
+ |
+ /* DEBUG */ |
+ // echo " $impl=$thisImplVer "; |
+ |
+ if (preg_match('/^-?$/', $thisImplVer) || self::_versionIsGreater($thisImplVer, $safeVer)) |
+ { |
+ return ''; |
+ } |
+ } |
+ |
+ return ' class="safe"'; |
+ } |
+ else |
+ { |
+ return ''; |
+ } |
+ } |
+ |
+ protected function getTitleStr() |
+ { |
+ if (!empty($this->title)) |
+ { |
+ return " title=\"{$this->title}\""; |
+ } |
+ else |
+ { |
+ return ''; |
+ } |
+ } |
+ |
+ protected function getAnchors() |
+ { |
+ $result = array(); |
+ |
+ foreach ($this->anchors as $anchor) |
+ { |
+ $result[] = "<a name=\"{$anchor}\""; |
+ |
+ if (preg_match('/^[a-z][a-z0-9_:.-]*/i', $anchor)) |
+ { |
+ $result[] = " id=\"{$anchor}\""; |
+ } |
+ |
+ $result[] = '></a>'; |
+ } |
+ |
+ return join('', $result); |
+ } |
+ |
+ protected function getAssumed($v) |
+ { |
+ if (is_array($v) && isset($v['assumed']) && $v['assumed']) |
+ { |
+ return ' class="assumed"'; |
+ } |
+ |
+ return ''; |
+ } |
+ |
+ protected function getTested($v) |
+ { |
+ if (is_array($v) && isset($v['tested']) && $v['tested']) |
+ { |
+ return ' class="tested"'; |
+ } |
+ |
+ return ''; |
+ } |
+ |
+ /** |
+ * Returns the version of a feature. |
+ * |
+ * @param string|VersionInfo $vInfo |
+ * @return mixed |
+ */ |
+ protected function getVer($vInfo) |
+ { |
+ if (is_array($vInfo)) |
+ { |
+ /* TODO: Return all versions: documented, assumed, and tested */ |
+ $vNumber = (isset($vInfo['tested']) |
+ && gettype($vInfo['tested']) !== 'boolean') |
+ ? $vInfo['tested'] |
+ : $vInfo[0]; |
+ $section = isset($vInfo['section']) |
+ ? ' <span class="section" title="Specification section">[' |
+ . $vInfo['section'] . ']</span>' |
+ : ''; |
+ |
+ if (isset($vInfo['urn'])) |
+ { |
+ if ($this->list instanceof FeatureList) |
+ { |
+ $url = $this->list->resolveURN($vInfo['urn']); |
+ $vNumber = '<a href="' . $url . '">' . $vNumber |
+ . ($section ? $section : '') . '</a>'; |
+ } |
+ } |
+ else if ($section) |
+ { |
+ $vNumber .= $section; |
+ } |
+ |
+ $vInfo = $vNumber; |
+ } |
+ |
+ return ($vInfo === '-') |
+ ? '<span title="Not supported">−</span>' |
+ : $vInfo; |
+ } |
+ |
+ /** |
+ * Returns a syntax-highlighted version of a string |
+ * |
+ * @param string $s |
+ * @return string |
+ */ |
+ protected static function shl($s) |
+ { |
+ /* stub */ |
+ return $s; |
+ } |
+ |
+ public function printMe() |
+ { |
+ ?> |
+<tr<?php echo $this->getSafeStr(); ?>> |
+ <th<?php echo $this->getTitleStr(); ?>><?php |
+ echo $this->getAnchors(); |
+ echo /*preg_replace_callback( |
+ '#(<code>)(.+?)(</code>)#', |
+ array('self', 'shl'),*/ |
+ preg_replace('/…/', '…', $this->content)/*)*/; |
+ ?></th> |
+<?php |
+ $versions = $this->versions; |
+ $testcase = false; |
+ if (!is_null($this->list)) |
+ { |
+ $versions =& $this->list->versions; |
+ $testcase = $this->list->testcase; |
+ } |
+ |
+ static $row = 0; |
+ $row++; |
+ |
+ $column = 0; |
+ $thisVersions =& $this->versions; |
+ |
+ foreach ($versions as $key => $value) |
+ { |
+ $column++; |
+ $id = "td$row-$column"; |
+ $ver = isset($thisVersions[$key]) ? $thisVersions[$key] : ''; |
+ if ($key || $testcase) |
+ { |
+?> |
+ <td<?php |
+ if (!$key) |
+ { |
+ echo " id='$id'"; |
+ } |
+ |
+ echo $this->getAssumed($ver) . $this->getTested($ver); |
+ |
+ if (!$key) |
+ { |
+ if (!empty($ver)) |
+ { |
+ echo ' title="Test code: ' |
+ . htmlspecialchars( |
+ preg_replace('/\\\(["\'])/', '\1', |
+ reduceWhitespace($ver) |
+ ), |
+ ENT_COMPAT, |
+ FEATURES_ENCODING |
+ ) |
+ . '"'; |
+ } |
+ else |
+ { |
+ echo ' title="Not applicable: No automated test case' |
+ . ' is available for this feature. If possible, please' |
+ . ' click the feature code in the first column to run' |
+ . ' a manual test."'; |
+ } |
+ } |
+ else |
+ { |
+ echo ' title="' |
+ . htmlspecialchars( |
+ preg_replace('/<.*?>/', '', $value), |
+ ENT_COMPAT, FEATURES_ENCODING) |
+ . '"'; |
+ } |
+ ?>><?php |
+ if ($key) |
+ { |
+ echo $this->getVer($ver); |
+ |
+ /* General footnotes support: include footnotes.class.php to enable */ |
+ if (is_array($ver) && isset($ver['footnote']) && $ver['footnote']) |
+ { |
+ echo $ver['footnote']; |
+ } |
+ } |
+ else |
+ { |
+ if (!empty($ver) && $testcase) |
+ { |
+ ?><script type="text/javascript"> |
+ // <![CDATA[ |
+ var s = test(<?php echo $ver; ?>, '<span title="Supported">+<\/span>', |
+ '<span title="Not supported">−<\/span>'); |
+ jsx.tryThis("document.write(s);", |
+ "document.getElementById('<?php echo $id; ?>').appendChild(" |
+ + "document.createTextNode(s));"); |
+ // ]]> |
+</script><?php |
+ } |
+ else |
+ { |
+ echo '<abbr>N/A</abbr>'; |
+ } |
+ } |
+ ?></td> |
+<?php |
+ } |
+ } |
+?> |
+ </tr> |
+<?php |
+ } |
+} |
+ |
+?> |
Index: trunk/.settings/org.eclipse.php.core.prefs |
=================================================================== |
--- trunk/.settings/org.eclipse.php.core.prefs (nonexistent) |
+++ trunk/.settings/org.eclipse.php.core.prefs (revision 80) |
@@ -0,0 +1,2 @@ |
+eclipse.preferences.version=1 |
+include_path=0;/PHPX |
/trunk/.settings/org.eclipse.php.core.prefs |
---|
Property changes: |
Added: svn:mime-type |
## -0,0 +1 ## |
+text/plain |
\ No newline at end of property |
Index: trunk/global.inc |
=================================================================== |
--- trunk/global.inc (nonexistent) |
+++ trunk/global.inc (revision 80) |
@@ -0,0 +1,533 @@ |
+<?php |
+ |
+/* @section Helper functions */ |
+ |
+/** |
+ * Retrieves the value of an element of an associative array. |
+ * |
+ * This is designed for $_GET and other HTTP variables but |
+ * also works for common associative arrays. |
+ * |
+ * @param $key: string |
+ * Key identifier. The default is the empty string. |
+ * @param $array: string |
+ * Array identifier. The default is '_GET'. If there is |
+ * no such array, the identifier is prefixed with 'HTTP' |
+ * and suffixed with '_VARS' (to support the deprecated |
+ * HTTP_GET_VARS etc. arrays as of PHP < 4.1). If |
+ * there is still no array with that identifier, return |
+ * the empty string. |
+ * @param $default: string |
+ * Default return value if the element specified with |
+ * $key is not available in the array. The default |
+ * is the empty string. |
+ * @return |
+ * The value of the element of that array with that key or |
+ * the empty string if there is no such element or array. |
+ * @author |
+ * Copyright (C) 2004, 2005 Thomas Lahn <php@PointedEars.de> |
+ */ |
+function getVars($key = '', $array = '_GET', $default = '', $noEntities = false) |
+{ |
+ global ${$array}; |
+ if (!isset(${'HTTP'.$array.'_VARS'})) global ${'HTTP'.$array.'_VARS'}; |
+/* |
+ echo "<pre>getVars: \$$array"."['$key']: return '" |
+ .(isset(${$array}) && isset(${$array}[$key]) |
+ ? ${$array}[$key] |
+ : (isset(${'HTTP'.$array.'_VARS'}) && isset(${'HTTP'.$array.'_VARS'}[$key]) |
+ ? ${'HTTP'.$array.'_VARS'}[$key] |
+ : $default)) . "'</pre><br>\n"; |
+*/ |
+ $result = (isset(${$array}) && isset(${$array}[$key]) |
+ ? ${$array}[$key] |
+ : (isset(${'HTTP'.$array.'_VARS'}) && isset(${'HTTP'.$array.'_VARS'}[$key]) |
+ ? ${'HTTP'.$array.'_VARS'}[$key] |
+ : $default)); |
+ |
+// TODO: Escape HTML entities |
+/* |
+ if (!$noEntities) |
+ { |
+ $result = htmlentities($result); |
+ } |
+*/ |
+ return $result; |
+} |
+ |
+/** |
+ * Converts the argument to a visible (X)HTML hyperlink |
+ * where its URI target is created from the argument. |
+ * Supported are e-mail addresses, domain names with |
+ * optional paths, and valid URIs. |
+ * |
+ * @param $text |
+ * Argument to be converted. |
+ * @return |
+ * The converted argument if it applies to a supported |
+ * scheme, the unconverted argument otherwise. |
+ * |
+ * @author (C) 2001-04-04T02:03 |
+ * mark.young@vdhinc.com at http://php.net/manual/en/ref.strings.php |
+ * |
+ * Minor correction to my HTMLEncode function. |
+ * |
+ * @author 2002-08-29T09:00 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Added target="_blank" |
+ * - Added support for ftp(s)-URIs: (ht|f) |
+ * - Added support for search strings (?...=...&...=...), either |
+ * with or without HTML entities: \?=(&\w;|&) |
+ * - Removed enclosing nl2br call because of preformatted display |
+ * |
+ * @author 2003-12-30T14:18 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Removed target="_blank". |
+ * - Added PHPdoc. |
+ * |
+ * @author 2004-01-12T12:45 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Added support for #fragment_identifiers in URIs. |
+ * - Added support for bugs and comments. |
+ * |
+ * @author 2004-01-13T01:29 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Added support for bug aliases. |
+ * |
+ * @author 2004-01-26T20:43 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Do not convert URIs in A elements. |
+ * - Do not allow URIs with "&" before search-string. |
+ * - camelCased function identifier. Only classes |
+ * and constructors should start with uppercase. |
+ * |
+ * @author 2004-01-27T14:07 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Allow to convert URIs preceded by ">" but not followed by "</a>". |
+ * - Allow ";" to be part of the search string |
+ * |
+ * @author 2004-01-29T14:10 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Require valid domain name for "bar" on conversion of "foo@bar" and |
+ * "www.bar". |
+ * - Be case-insensitive except of bug aliases |
+ * - Escaped "-" in character classes if not meant as range metacharacter. |
+ * - Corrected year. |
+ * |
+ * @author 2004-02-14T17:37 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Accept only valid Internet domain names |
+ * - Accept "%" within path and query part (to escape ASCII characters) |
+ * |
+ * @author 2004-02-27T19:21 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Allow unescaped ":" in URI components, since it apparently does not |
+ * "conflict with the reserved purpose" (RFC 2396, section 2.2.; here: |
+ * scheme component) |
+ * - Allow slashes, dots and dashes in URI components |
+ * - Removed invalid [...(...|...)...] |
+ * |
+ * @author 2004-03-01T21:48 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Allow IPv4 addresses |
+ * |
+ * @author 2004-03-08T02:20 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Allow "+" and "," in query part |
+ * - Optimized character classes |
+ * |
+ * @author (C) 2004-04-23T10:03 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Rewrite to use RFC 2822 and 2369 syntax |
+ */ |
+function htmlEncode($text) |
+{ |
+ // see RFC 2822 "Internet Message Format" |
+ $local_part = '[^()<>@,;:\\"\-\[\] \x00-\x1A\x7F]+'; |
+ |
+ // see RFC 2396 "Uniform Resource Identifiers (URI): Generic Syntax" |
+ // $digit = '\d'; // 0-9; def. not required |
+ // $hex = '\da-f'; // "hex" for case-insensitive match; def. N/R |
+ $alpha = 'a-z'; // "alpha" for case-insensitive match |
+ $alphanum = $alpha.'\d'; // "alphanum" for case-insensitive match |
+ $mark = "\-_.!~*\'()"; |
+ $reserved = ';/?:@&=+$,'; |
+ $unreserved = $alphanum.$mark; |
+ $escaped = '%[\da-f]'; // contains $hex |
+ |
+ // added (?!gt;) to allow "<URI>" |
+ $uric = '(['.$reserved.$alphanum.$mark.'](?!gt;)|'.$escaped.')'; |
+ $uric_no_slash = '(['.$unreserved.';\?:@&=+$,](?!gt;)|'.$escaped.')'; |
+ $pchar = '(['.$unreserved.':@&=+$,](?!gt;)|'.$escaped.')'; |
+ $param = $pchar; |
+ $segment = $pchar.'*(;'.$param.')*'; |
+ $abs_path = '/' . $segment . '(/'.$segment.')*'; |
+ $userinfo = '(['.$unreserved.';:&=+$,](?!gt;)|'.$escaped.')*'; |
+ $domainlabel = '(['.$alphanum.']' |
+ . '|['.$alphanum.'](['.$alphanum.']|-)*['.$alphanum.']' |
+ . ')'; |
+ $toplabel = '(['.$alpha.']' |
+ . '|['.$alpha.'](['.$alphanum.']|-)*['.$alphanum. '])'; |
+ $hostname = '('.$domainlabel. '\.)*' . $toplabel . '\.?'; |
+ $ipv4_address = '\d+\.\d+\.\d+\.\d+'; |
+ $host = '(' . $hostname . '|' . $ipv4_address . ')'; |
+ $port = '\d*'; |
+ $hostport = $host . '(:' . $port . ')?'; |
+ $server_req = '(' . $userinfo . ')?' . $hostport; // server is required |
+ $reg_name = '([' . $unreserved . '$,;:@&=+](?!gt;)|' . $escaped . ')+'; |
+ $authority = '(' . $server_req . '|' . $reg_name . ')'; |
+ $net_path = '//' . $authority . '('.$abs_path.')?'; |
+ $query = $uric.'*'; |
+ $scheme = '(ht|f)tps?'; |
+ $hier_part = '(' . $net_path .'|' . $abs_path . ')(\?' . $query . ')?'; |
+ $opaque_part = $uric_no_slash . $uric.'*'; |
+ $absolute_uri = $scheme . ':(' . $hier_part . '|' . $opaque_part . ')'; |
+ $fragment = $uric.'*'; |
+ |
+ // absolute URIs only |
+ $uri_reference = $absolute_uri . '(#' . $fragment . ')?'; |
+ // echo '<br>'.htmlentities($local_part . '@' . $host).'<br>'; |
+ |
+ $searcharray = array( |
+ "'(?i)(" . $local_part . '@' . $host . ")'i", |
+ "'(?i)((?:(?!://).{3}|^.{0,2}))(www\." . $hostname |
+ . '|' . $ipv4_address . ")'i", |
+ "'(?i)(?<!href=\")(" . $uri_reference . ")(?!</a>)'i", |
+ "'(((?i)bug)\s+#?([\dA-Z_]+)\s+((?i)comment|Kommentar)\s+#?(\d+))'", |
+ "'(((?i)bug)\s+#?([\dA-Z_]+))'", |
+ "'(((?i)comment|Kommentar)\s+#?(\d+))'" |
+ ); |
+ |
+ $replacearray = array( |
+ "<a href=\"mailto:\\1\">\\1</a>", |
+ "\\1http://\\2", |
+ "<a href=\"\\1\">\\1</a>", |
+ "<a href=\"./?bug=\\3#c\\5\">\\1</a>", |
+ "<a href=\"./?bug=\\3#details\">\\1</a>", |
+ "<a href=\"#c\\3\">\\1</a>" |
+ ); |
+ |
+ return preg_replace($searcharray, $replacearray, $text); |
+} |
+ |
+/** |
+ * Converts HTML entities to real characters using the detected |
+ * or specified character encoding. |
+ * |
+ * @param string $s |
+ * @param int[optional] $quote_style |
+ * @return string |
+ */ |
+function htmlEntityDecode($s, $quote_style=ENT_COMPAT, $encoding=null) |
+{ |
+ $s = (string) $s; |
+ |
+ if (is_null($encoding)) |
+ { |
+ $encoding = mb_detect_encoding($s); |
+ if ($encoding === 'ASCII') |
+ { |
+ $encoding = 'ISO-8859-1'; |
+ } |
+ } |
+ |
+ return html_entity_decode($s, $quote_style, $encoding); |
+} |
+ |
+ |
+/** |
+ * Converts the argument into a visible (X)HTML hyperlink if a condition |
+ * applies. |
+ * |
+ * @author |
+ * (C) 2003, 2004 Thomas Lahn <selfhtml.de@PointedEars.de> |
+ * @param $s |
+ * Content to be converted. Required. |
+ * @param $sCond |
+ * Condition to be true for the content to be converted. |
+ * The default is <code>true</code>. |
+ * @param $sURI |
+ * Target URI of the hyperlink. The default is the |
+ * value of $s. |
+ * @param $sName |
+ * Value of the <code>name</code> attribute of the |
+ * <code>a</code> element. Unused if not provided |
+ * or empty. |
+ * @param $sTitle |
+ * Value of the <code>title</code> attribute of the |
+ * <code>a</code> element. Unused if not provided |
+ * or empty. |
+ * @param $sClass |
+ * Value of the <code>class</code> attribute of the |
+ * <code>a</code> element. Unused if not provided |
+ * or empty. |
+ * @return |
+ * The converted argument if the condition applies, |
+ * the unconverted argument otherwise. |
+ */ |
+function makeLinkIf( |
+ $s, |
+ $sCond = true, |
+ $sURI = NULL, |
+ $sTarget = '', |
+ $sName = '', |
+ $sTitle = '', |
+ $sClass = '') |
+{ |
+ return ($sCond || $sName |
+ ? '<a' . ($sCond |
+ ? " href=\"" . (is_null($sURI) ? $s : $sURI) |
+ . "\"".($sTarget ? " target=\"$sTarget\"" : '') |
+ : '' |
+ ) |
+ . ($sName ? " name=\"$sName\"" : '') |
+ . ($sTitle ? " title=\"$sTitle\"" : '') |
+ . ($sClass ? " class=\"$sClass\"" : '') |
+ . '>' |
+ : '' |
+ ) |
+ . $s |
+ . ($sCond || $sName ? '</a>' : ''); |
+} |
+ |
+/** |
+ * Returns a visible (X)HTML hyperlink that uses the mailto: URI scheme. |
+ * |
+ * @author (C) 2003 Thomas Lahn <selfhtml.de@PointedEars.de> |
+ * |
+ * @author (C) 2003-12-30 Thomas Lahn <selfhtml.de@PointedEars.de> |
+ * - Corrected `$email ($name)'. |
+ * - Now uses rawurlencode(...). |
+ * - Added PHPdoc. |
+ * |
+ * @author (C) 2004-11-30 Thomas Lahn <selfhtml.de@PointedEars.de> |
+ * - Don't rawurlencode(...) parens for comments. |
+ * |
+ * @param $sAddress |
+ * E-mail address. The default is <selfhtml.de@PointedEars.de>. |
+ * @param $sTitle |
+ * Value of the <code>title</code> attribute of the |
+ * <code>a</code> element. Unused if not provided |
+ * or empty. The default is 'PointedEars'. |
+ * @param $sToName |
+ * Name to be used in the To header of the e-mail. |
+ * Note that @link{rawurlencode()} is used to escape |
+ * special characters automatically. |
+ * The default is "Thomas 'PointedEars' Lahn". |
+ * |
+ * @return |
+ * The converted argument if the condition applies, |
+ * the unconverted argument otherwise. |
+ */ |
+function mailto_link( |
+ $sAddress = 'selfhtml.de@PointedEars.de', |
+ $sTitle = 'PointedEars', |
+ $sToName = "Thomas 'PointedEars' Lahn", |
+ $sSubject = 'SELFbug', |
+ $sImgPath = '../../media/mail.gif' |
+) |
+{ |
+//width="14" height="15" // image size detection not yet implemented |
+ return ($sImgPath != '' |
+ ? '<img src="' . $sImgPath . '" border="0" alt="@"> ' |
+ : '') |
+ . '<a href="mailto:' |
+ . ($sAddress != '' |
+ ? $sAddress |
+ : 'selfhtml.de@PointedEars.de') |
+ . ($sToName != '' |
+ ? ' (' . rawurlencode($sToName) . ')' |
+ : '') |
+ . ($sSubject != '' |
+ ? '?subject=' . rawurlencode($sSubject) |
+ : '') |
+ . '">' . (($sTitle != '') ? $sTitle : $sAddress) . '</a>'; |
+} |
+ |
+// map bug states to numbers so that they become comparable |
+function array_values_to_keys($a) |
+{ |
+ $aNew = array(); |
+ foreach ($a as $key => $value) |
+ { |
+ $aNew[$value] = count($aNew); |
+ } |
+ return $aNew; |
+} |
+ |
+/** |
+ * Maps an array to another array using a callback function. |
+ * |
+ * Unlike array_map(), the callback is called with three parameters: the value |
+ * to be processed, the key of that value, and the array processed. The return |
+ * value of the callback defines the value for the same key of the returned |
+ * array. |
+ * |
+ * Because of the way the callback is called, this method supports processing |
+ * only one array at a time, unlike array_map(). Unlike array_walk(), this |
+ * function does not modify the original array. |
+ * |
+ * @param string|array $callback |
+ * The callback to be used for mapping array values. If an array, the first |
+ * element of the array is supposed to be the class name, and the second |
+ * element the method name of a static method to be used. |
+ * @param array $array |
+ * The array to process |
+ * @return array |
+ * @see array_map() |
+ * @see array_walk() |
+ */ |
+function array_map2($callback, $array) |
+{ |
+ $a = array(); |
+ |
+ if (is_array($callback)) |
+ { |
+ list($class, $method) = $callback; |
+ } |
+ |
+ foreach ($array as $key => &$value) |
+ { |
+ if (is_array($callback)) |
+ { |
+ $a[$key] = $class::$method($value, $key, $array); |
+ } |
+ else |
+ { |
+ $a[$key] = $callback($value, $key, $array); |
+ } |
+ } |
+ |
+ return $a; |
+} |
+ |
+/** |
+ * Converts a string or an array of strings to an associative |
+ * bitmask array with the string(s) as key(s). |
+ * |
+ * Converts the argument to a bitmask array where each member's |
+ * value is a power of 2, so that arbitrary member values can be |
+ * added to an integer on which bitwise operations with the member |
+ * value or a combination of member values are possible. |
+ * |
+ * @author (c) 2003 Thomas Lahn <SELFbug@PointedEars.de> |
+ * @param $aArray |
+ * String or array of strings to be converted. |
+ */ |
+function getBitmaskArray($aArray) |
+{ |
+ $a = array(); |
+ |
+ if (is_array($aArray)) |
+ { |
+ for ($i = 0; $i < count($aArray); $i++) |
+ { |
+ $a[$aArray[$i]] = pow(2, $i); |
+ } |
+ } |
+ else |
+ $a[$aArray] = 1; |
+ |
+ return $a; |
+} |
+ |
+/** |
+ * Returns the contents of a file as if include() was used. |
+ * |
+ * @param string $filename Path of the file to retrieve |
+ * @return string File contents |
+ */ |
+function get_include_content($filename) |
+{ |
+ if (is_file($filename)) |
+ { |
+ ob_start(); |
+ include $filename; |
+ $contents = ob_get_contents(); |
+ ob_end_clean(); |
+ return $contents; |
+ } |
+ |
+ return ''; |
+} |
+ |
+/** |
+ * Replaces each group of expressions in a string with the same |
+ * corresponding string. |
+ * |
+ * @param Array[Array[string] | string, string] $map |
+ * @param string $subject |
+ * @return string |
+ * A copy of $subject with the provided mapping applied. |
+ */ |
+function preg_replace_group($map = array(), $subject = '') |
+{ |
+ if ($subject) |
+ { |
+ for ($i = 0, $len = count($map); $i < $len; $i++) |
+ { |
+ $subject = preg_replace($map[$i][0], $map[$i][1], $subject); |
+ } |
+ } |
+ |
+ return $subject; |
+} |
+ |
+/** |
+ * Randomly encodes a string of characters. |
+ * |
+ * @param string $s |
+ * String to be encoded |
+ * @param string $format = 'sgml' |
+ * Encoding format. Currently only SGML-based encoding of |
+ * ASCII characters with character references is supported. |
+ * @return string |
+ */ |
+function randomEsc($s = '', $format = 'sgml') |
+{ |
+ $f = function_exists('mt_rand') ? 'mt_rand' : 'rand'; |
+ |
+ return preg_replace_callback('/[\\x00-\\x7F]/', |
+ create_function('$m', "return $f(0, 1)" . '? $m[0] : "&#" . ord($m[0]) . ";";'), |
+ $s); |
+} |
+ |
+/** |
+ * Reduces sequences of two or more consecutive white-space characters |
+ * in an input to a single space. |
+ * |
+ * @param string $s |
+ * @return string |
+ */ |
+function reduceWhitespace($s) |
+{ |
+ return preg_replace('/\s{2,}/', ' ', $s); |
+} |
+ |
+function debug($x) |
+{ |
+ echo '<pre>'; |
+ |
+// if (is_array($x)) |
+// { |
+// print_r($x); |
+// } |
+// else |
+// { |
+ var_dump($x); |
+// } |
+ |
+ echo '</pre>'; |
+} |
Index: trunk/.project |
=================================================================== |
--- trunk/.project (nonexistent) |
+++ trunk/.project (revision 80) |
@@ -0,0 +1,22 @@ |
+<?xml version="1.0" encoding="UTF-8"?> |
+<projectDescription> |
+ <name>PHPX</name> |
+ <comment></comment> |
+ <projects> |
+ </projects> |
+ <buildSpec> |
+ <buildCommand> |
+ <name>org.eclipse.wst.validation.validationbuilder</name> |
+ <arguments> |
+ </arguments> |
+ </buildCommand> |
+ <buildCommand> |
+ <name>org.eclipse.dltk.core.scriptbuilder</name> |
+ <arguments> |
+ </arguments> |
+ </buildCommand> |
+ </buildSpec> |
+ <natures> |
+ <nature>org.eclipse.php.core.PHPNature</nature> |
+ </natures> |
+</projectDescription> |
/trunk/.project |
---|
Property changes: |
Added: svn:mime-type |
## -0,0 +1 ## |
+text/plain |
\ No newline at end of property |
Index: trunk/htmlencode.old.php |
=================================================================== |
--- trunk/htmlencode.old.php (nonexistent) |
+++ trunk/htmlencode.old.php (revision 80) |
@@ -0,0 +1,117 @@ |
+/** |
+ * Converts the argument to a visible (X)HTML hyperlink |
+ * where its URI target is created from the argument. |
+ * Supported are e-mail addresses, domain names with |
+ * optional paths, and valid URIs. |
+ * |
+ * @param $text |
+ * Argument to be converted. |
+ * @return |
+ * The converted argument if it applies to a supported |
+ * scheme, the unconverted argument otherwise. |
+ * |
+ * @author (C) 2001-04-04T02:03 |
+ * mark.young@vdhinc.com at http://php.net/manual/en/ref.strings.php |
+ * |
+ * Minor correction to my HTMLEncode function. |
+ * |
+ * @author 2002-08-29T09:00 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Added target="_blank" |
+ * - Added support for ftp(s)-URIs: (ht|f) |
+ * - Added support for search strings (?...=...&...=...), either |
+ * with or without HTML entities: \?=(&\w;|&) |
+ * - Removed enclosing nl2br call because of preformatted display |
+ * |
+ * @author 2003-12-30T14:18 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Removed target="_blank". |
+ * - Added PHPdoc. |
+ * |
+ * @author 2004-01-12T12:45 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Added support for #fragment_identifiers in URIs. |
+ * - Added support for bugs and comments. |
+ * |
+ * @author 2004-01-13T01:29 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Added support for bug aliases. |
+ * |
+ * @author 2004-01-26T20:43 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Do not convert URIs in A elements. |
+ * - Do not allow URIs with "&" before search-string. |
+ * - camelCased function identifier. Only classes |
+ * and constructors should start with uppercase. |
+ * |
+ * @author 2004-01-27T14:07 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Allow to convert URIs preceded by ">" but not followed by "</a>". |
+ * - Allow ";" to be part of the search string |
+ * |
+ * @author 2004-01-29T14:10 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Require valid domain name for "bar" on conversion of "foo@bar" and |
+ * "www.bar". |
+ * - Be case-insensitive except of bug aliases |
+ * - Escaped "-" in character classes if not meant as range metacharacter. |
+ * - Corrected year. |
+ * |
+ * @author 2004-02-14T17:37 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Accept only valid Internet domain names |
+ * - Accept "%" within path and query part (to escape ASCII characters) |
+ * |
+ * @author 2004-02-27T19:21 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Allow unescaped ":" in URI components, since it apparently does not |
+ * "conflict with the reserved purpose" (RFC 2396, section 2.2.; here: |
+ * scheme component) |
+ * - Allow slashes, dots and dashes in URI components |
+ * - Removed invalid [...(...|...)...] |
+ * |
+ * @author 2004-03-01T21:48 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Allow IPv4 addresses |
+ * |
+ * @author 2004-03-08T02:20 |
+ * Thomas Lahn <PointedEars@selfhtml.de> at localhost |
+ * |
+ * - Allow "+" and "," in query part |
+ * - Optimized character classes |
+ */ |
+function htmlEncode($text) |
+{ |
+ $searcharray = array( |
+ "'(?i)([\-_\w\d.]+@[\-\w\d.]+\.[A-Z]{2,})'i", |
+ "'(?i)((?:(?!://).{3}|^.{0,2}))(www\.[\-\w\d.]+\.[A-Z]{2,}" |
+ . "|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'i", |
+ "'(?i)(?<!href=\")((ht|f)tps?:\/\/([\-\w\d.]+\.[A-Z]{2,}" |
+ . "|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})[#\-_~\w\d\.\/%]+" |
+ . "(\?([%&+-;=\w])*)?(;[=\d\w])?)(?!</a>)'i", |
+ "'(((?i)bug)\s+#?([\dA-Z_]+)\s+((?i)comment|Kommentar)\s+#?(\d+))'", |
+ "'(((?i)bug)\s+#?([\dA-Z_]+))'", |
+ "'(((?i)comment|Kommentar)\s+#?(\d+))'" |
+ ); |
+ |
+ $replacearray = array( |
+ "<a href=\"mailto:\\1\">\\1</a>", |
+ "\\1http://\\2", |
+ "<a href=\"\\1\">\\1</a>", |
+ "<a href=\"./?bug=\\3#c\\5\">\\1</a>", |
+ "<a href=\"./?bug=\\3#details\">\\1</a>", |
+ "<a href=\"#c\\3\">\\1</a>" |
+ ); |
+ |
+ return preg_replace($searcharray, $replacearray, $text); |
+} |
/trunk/htmlencode.old.php |
---|
Property changes: |
Added: svn:executable |
## -0,0 +1 ## |
+* |
\ No newline at end of property |
Index: trunk/.buildpath |
=================================================================== |
--- trunk/.buildpath (nonexistent) |
+++ trunk/.buildpath (revision 80) |
@@ -0,0 +1,5 @@ |
+<?xml version="1.0" encoding="UTF-8"?> |
+<buildpath> |
+ <buildpathentry kind="src" path=""/> |
+ <buildpathentry kind="con" path="org.eclipse.php.core.LANGUAGE"/> |
+</buildpath> |
Index: trunk |
=================================================================== |
--- trunk (revision 1) |
+++ trunk (revision 80) |
/trunk |
---|
Property changes: |
Added: svn:ignore |
## -0,0 +1,5 ## |
+Angi |
+ |
+PHPeclipse |
+ |
+PregCal |