MINOR Readded sapphire/thirdparty modules without svn:externals

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/branches/2.3@99765 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2010-02-24 00:53:55 +00:00 committed by Sam Minnee
parent add3165333
commit da691ecb0c
23 changed files with 14776 additions and 0 deletions

296
thirdparty/jsmin/JSMin.php vendored Normal file
View File

@ -0,0 +1,296 @@
<?php
/**
* jsmin.php - PHP implementation of Douglas Crockford's JSMin.
*
* This is pretty much a direct port of jsmin.c to PHP with just a few
* PHP-specific performance tweaks. Also, whereas jsmin.c reads from stdin and
* outputs to stdout, this library accepts a string as input and returns another
* string as output.
*
* PHP 5 or higher is required.
*
* Permission is hereby granted to use this version of the library under the
* same terms as jsmin.c, which has the following license:
*
* --
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* The Software shall be used for Good, not Evil.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* --
*
* @package JSMin
* @author Ryan Grove <ryan@wonko.com>
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
* @copyright 2007 Ryan Grove <ryan@wonko.com> (PHP port)
* @license http://opensource.org/licenses/mit-license.php MIT License
* @version 1.1.0 (2007-06-01)
* @link http://code.google.com/p/jsmin-php/
*/
class JSMin {
const ORD_LF = 10;
const ORD_SPACE = 32;
protected $a = '';
protected $b = '';
protected $input = '';
protected $inputIndex = 0;
protected $inputLength = 0;
protected $lookAhead = null;
protected $output = array();
// -- Public Static Methods --------------------------------------------------
public static function minify($js) {
$jsmin = new JSMin($js);
return $jsmin->min();
}
// -- Public Instance Methods ------------------------------------------------
public function __construct($input) {
$this->input = str_replace("\r\n", "\n", $input);
$this->inputLength = strlen($this->input);
}
// -- Protected Instance Methods ---------------------------------------------
protected function action($d) {
switch($d) {
case 1:
$this->output[] = $this->a;
case 2:
$this->a = $this->b;
if ($this->a === "'" || $this->a === '"') {
for (;;) {
$this->output[] = $this->a;
$this->a = $this->get();
if ($this->a === $this->b) {
break;
}
if (ord($this->a) <= self::ORD_LF) {
throw new JSMinException('Unterminated string literal.');
}
if ($this->a === '\\') {
$this->output[] = $this->a;
$this->a = $this->get();
}
}
}
case 3:
$this->b = $this->next();
if ($this->b === '/' && (
$this->a === '(' || $this->a === ',' || $this->a === '=' ||
$this->a === ':' || $this->a === '[' || $this->a === '!' ||
$this->a === '&' || $this->a === '|' || $this->a === '?')) {
$this->output[] = $this->a;
$this->output[] = $this->b;
for (;;) {
$this->a = $this->get();
if ($this->a === '/') {
break;
}
elseif ($this->a === '\\') {
$this->output[] = $this->a;
$this->a = $this->get();
}
elseif (ord($this->a) <= self::ORD_LF) {
throw new JSMinException('Unterminated regular expression '.
'literal.');
}
$this->output[] = $this->a;
}
$this->b = $this->next();
}
}
}
protected function get() {
$c = $this->lookAhead;
$this->lookAhead = null;
if ($c === null) {
if ($this->inputIndex < $this->inputLength) {
$c = $this->input[$this->inputIndex];
$this->inputIndex += 1;
}
else {
$c = null;
}
}
if ($c === "\r") {
return "\n";
}
if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
return $c;
}
return ' ';
}
protected function isAlphaNum($c) {
return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
}
protected function min() {
$this->a = "\n";
$this->action(3);
while ($this->a !== null) {
switch ($this->a) {
case ' ':
if ($this->isAlphaNum($this->b)) {
$this->action(1);
}
else {
$this->action(2);
}
break;
case "\n":
switch ($this->b) {
case '{':
case '[':
case '(':
case '+':
case '-':
$this->action(1);
break;
case ' ':
$this->action(3);
break;
default:
if ($this->isAlphaNum($this->b)) {
$this->action(1);
}
else {
$this->action(2);
}
}
break;
default:
switch ($this->b) {
case ' ':
if ($this->isAlphaNum($this->a)) {
$this->action(1);
break;
}
$this->action(3);
break;
case "\n":
switch ($this->a) {
case '}':
case ']':
case ')':
case '+':
case '-':
case '"':
case "'":
$this->action(1);
break;
default:
if ($this->isAlphaNum($this->a)) {
$this->action(1);
}
else {
$this->action(3);
}
}
break;
default:
$this->action(1);
break;
}
}
}
return implode('', $this->output);
}
protected function next() {
$c = $this->get();
if ($c === '/') {
switch($this->peek()) {
case '/':
for (;;) {
$c = $this->get();
if (ord($c) <= self::ORD_LF) {
return $c;
}
}
case '*':
$this->get();
for (;;) {
switch($this->get()) {
case '*':
if ($this->peek() === '/') {
$this->get();
return ' ';
}
break;
case null:
throw new JSMinException('Unterminated comment.');
}
}
default:
return $c;
}
}
return $c;
}
protected function peek() {
$this->lookAhead = $this->get();
return $this->lookAhead;
}
}
// -- Exceptions ---------------------------------------------------------------
class JSMinException extends Exception {}
?>

806
thirdparty/json/JSON.php vendored Normal file
View File

@ -0,0 +1,806 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Converts to and from JSON format.
*
* JSON (JavaScript Object Notation) is a lightweight data-interchange
* format. It is easy for humans to read and write. It is easy for machines
* to parse and generate. It is based on a subset of the JavaScript
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
* This feature can also be found in Python. JSON is a text format that is
* completely language independent but uses conventions that are familiar
* to programmers of the C-family of languages, including C, C++, C#, Java,
* JavaScript, Perl, TCL, and many others. These properties make JSON an
* ideal data-interchange language.
*
* This package provides a simple encoder and decoder for JSON notation. It
* is intended for use with client-side Javascript applications that make
* use of HTTPRequest to perform server communication functions - data can
* be encoded into JSON notation for use in a client-side javascript, or
* decoded from incoming Javascript requests. JSON format is native to
* Javascript, and can be directly eval()'ed with no further parsing
* overhead
*
* All strings should be in ASCII or UTF-8 format!
*
* LICENSE: Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met: Redistributions of source code must retain the
* above copyright notice, this list of conditions and the following
* disclaimer. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* @category
* @package Services_JSON
* @author Michal Migurski <mike-json@teczno.com>
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski
* @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
* @license http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_SLICE', 1);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_STR', 2);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_ARR', 3);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_OBJ', 4);
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_CMT', 5);
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_LOOSE_TYPE', 16);
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
/**
* Converts to and from JSON format.
*
* Brief example of use:
*
* <code>
* // create a new instance of Services_JSON
* $json = new Services_JSON();
*
* // convert a complexe value to JSON notation, and send it to the browser
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
* $output = $json->encode($value);
*
* print($output);
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
*
* // accept incoming POST data, assumed to be in JSON notation
* $input = file_get_contents('php://input', 1000000);
* $value = $json->decode($input);
* </code>
*/
class Services_JSON
{
/**
* constructs a new JSON instance
*
* @param int $use object behavior flags; combine with boolean-OR
*
* possible values:
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
* "{...}" syntax creates associative arrays
* instead of objects in decode().
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
* Values which can't be encoded (e.g. resources)
* appear as NULL instead of throwing errors.
* By default, a deeply-nested resource will
* bubble up with an error, so all return values
* from encode() should be checked with isError()
*/
function Services_JSON($use = 0)
{
$this->use = $use;
}
/**
* convert a string from one UTF-16 char to one UTF-8 char
*
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
*
* @param string $utf16 UTF-16 character
* @return string UTF-8 character
* @access private
*/
function utf162utf8($utf16)
{
// oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
}
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
switch(true) {
case ((0x7F & $bytes) == $bytes):
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x7F & $bytes);
case (0x07FF & $bytes) == $bytes:
// return a 2-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xC0 | (($bytes >> 6) & 0x1F))
. chr(0x80 | ($bytes & 0x3F));
case (0xFFFF & $bytes) == $bytes:
// return a 3-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xE0 | (($bytes >> 12) & 0x0F))
. chr(0x80 | (($bytes >> 6) & 0x3F))
. chr(0x80 | ($bytes & 0x3F));
}
// ignoring UTF-32 for now, sorry
return '';
}
/**
* convert a string from one UTF-8 char to one UTF-16 char
*
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
*
* @param string $utf8 UTF-8 character
* @return string UTF-16 character
* @access private
*/
function utf82utf16($utf8)
{
// oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
}
switch(strlen($utf8)) {
case 1:
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return $utf8;
case 2:
// return a UTF-16 character from a 2-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x07 & (ord($utf8{0}) >> 2))
. chr((0xC0 & (ord($utf8{0}) << 6))
| (0x3F & ord($utf8{1})));
case 3:
// return a UTF-16 character from a 3-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr((0xF0 & (ord($utf8{0}) << 4))
| (0x0F & (ord($utf8{1}) >> 2)))
. chr((0xC0 & (ord($utf8{1}) << 6))
| (0x7F & ord($utf8{2})));
}
// ignoring UTF-32 for now, sorry
return '';
}
/**
* encodes an arbitrary variable into JSON format
*
* @param mixed $var any number, boolean, string, array, or object to be encoded.
* see argument 1 to Services_JSON() above for array-parsing behavior.
* if var is a strng, note that encode() always expects it
* to be in ASCII or UTF-8 format!
*
* @return mixed JSON string representation of input var or an error if a problem occurs
* @access public
*/
function encode($var)
{
switch (gettype($var)) {
case 'boolean':
return $var ? 'true' : 'false';
case 'NULL':
return 'null';
case 'integer':
return (int) $var;
case 'double':
case 'float':
return (float) $var;
case 'string':
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
$ascii = '';
$strlen_var = strlen($var);
/*
* Iterate over every character in the string,
* escaping with a slash or encoding to UTF-8 where necessary
*/
for ($c = 0; $c < $strlen_var; ++$c) {
$ord_var_c = ord($var{$c});
switch (true) {
case $ord_var_c == 0x08:
$ascii .= '\b';
break;
case $ord_var_c == 0x09:
$ascii .= '\t';
break;
case $ord_var_c == 0x0A:
$ascii .= '\n';
break;
case $ord_var_c == 0x0C:
$ascii .= '\f';
break;
case $ord_var_c == 0x0D:
$ascii .= '\r';
break;
case $ord_var_c == 0x22:
case $ord_var_c == 0x2F:
case $ord_var_c == 0x5C:
// double quote, slash, slosh
$ascii .= '\\'.$var{$c};
break;
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
// characters U-00000000 - U-0000007F (same as ASCII)
$ascii .= $var{$c};
break;
case (($ord_var_c & 0xE0) == 0xC0):
// characters U-00000080 - U-000007FF, mask 110XXXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
$c += 1;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xF0) == 0xE0):
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}));
$c += 2;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xF8) == 0xF0):
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}));
$c += 3;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xFC) == 0xF8):
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}),
ord($var{$c + 4}));
$c += 4;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ord_var_c & 0xFE) == 0xFC):
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}),
ord($var{$c + 4}),
ord($var{$c + 5}));
$c += 5;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
}
}
return '"'.$ascii.'"';
case 'array':
/*
* As per JSON spec if any array key is not an integer
* we must treat the the whole array as an object. We
* also try to catch a sparsely populated associative
* array with numeric keys here because some JS engines
* will create an array with empty indexes up to
* max_index which can cause memory issues and because
* the keys, which may be relevant, will be remapped
* otherwise.
*
* As per the ECMA and JSON specification an object may
* have any string as a property. Unfortunately due to
* a hole in the ECMA specification if the key is a
* ECMA reserved word or starts with a digit the
* parameter is only accessible using ECMAScript's
* bracket notation.
*/
// treat as a JSON object
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
$properties = array_map(array($this, 'name_value'),
array_keys($var),
array_values($var));
foreach($properties as $property) {
if(Services_JSON::isError($property)) {
return $property;
}
}
return '{' . join(',', $properties) . '}';
}
// treat it like a regular array
$elements = array_map(array($this, 'encode'), $var);
foreach($elements as $element) {
if(Services_JSON::isError($element)) {
return $element;
}
}
return '[' . join(',', $elements) . ']';
case 'object':
$vars = get_object_vars($var);
$properties = array_map(array($this, 'name_value'),
array_keys($vars),
array_values($vars));
foreach($properties as $property) {
if(Services_JSON::isError($property)) {
return $property;
}
}
return '{' . join(',', $properties) . '}';
default:
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
? 'null'
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
}
}
/**
* array-walking function for use in generating JSON-formatted name-value pairs
*
* @param string $name name of key to use
* @param mixed $value reference to an array element to be encoded
*
* @return string JSON-formatted name-value pair, like '"name":value'
* @access private
*/
function name_value($name, $value)
{
$encoded_value = $this->encode($value);
if(Services_JSON::isError($encoded_value)) {
return $encoded_value;
}
return $this->encode(strval($name)) . ':' . $encoded_value;
}
/**
* reduce a string by removing leading and trailing comments and whitespace
*
* @param $str string string value to strip of comments and whitespace
*
* @return string string value stripped of comments and whitespace
* @access private
*/
function reduce_string($str)
{
$str = preg_replace(array(
// eliminate single line comments in '// ...' form
'#^\s*//(.+)$#m',
// eliminate multi-line comments in '/* ... */' form, at start of string
'#^\s*/\*(.+)\*/#Us',
// eliminate multi-line comments in '/* ... */' form, at end of string
'#/\*(.+)\*/\s*$#Us'
), '', $str);
// eliminate extraneous space
return trim($str);
}
/**
* decodes a JSON string into appropriate variable
*
* @param string $str JSON-formatted string
*
* @return mixed number, boolean, string, array, or object
* corresponding to given JSON input string.
* See argument 1 to Services_JSON() above for object-output behavior.
* Note that decode() always returns strings
* in ASCII or UTF-8 format!
* @access public
*/
function decode($str)
{
$str = $this->reduce_string($str);
switch (strtolower($str)) {
case 'true':
return true;
case 'false':
return false;
case 'null':
return null;
default:
$m = array();
if (is_numeric($str)) {
// Lookie-loo, it's a number
// This would work on its own, but I'm trying to be
// good about returning integers where appropriate:
// return (float)$str;
// Return float or int, as appropriate
return ((float)$str == (integer)$str)
? (integer)$str
: (float)$str;
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
// STRINGS RETURNED IN UTF-8 FORMAT
$delim = substr($str, 0, 1);
$chrs = substr($str, 1, -1);
$utf8 = '';
$strlen_chrs = strlen($chrs);
for ($c = 0; $c < $strlen_chrs; ++$c) {
$substr_chrs_c_2 = substr($chrs, $c, 2);
$ord_chrs_c = ord($chrs{$c});
switch (true) {
case $substr_chrs_c_2 == '\b':
$utf8 .= chr(0x08);
++$c;
break;
case $substr_chrs_c_2 == '\t':
$utf8 .= chr(0x09);
++$c;
break;
case $substr_chrs_c_2 == '\n':
$utf8 .= chr(0x0A);
++$c;
break;
case $substr_chrs_c_2 == '\f':
$utf8 .= chr(0x0C);
++$c;
break;
case $substr_chrs_c_2 == '\r':
$utf8 .= chr(0x0D);
++$c;
break;
case $substr_chrs_c_2 == '\\"':
case $substr_chrs_c_2 == '\\\'':
case $substr_chrs_c_2 == '\\\\':
case $substr_chrs_c_2 == '\\/':
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
$utf8 .= $chrs{++$c};
}
break;
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
// single, escaped unicode character
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
. chr(hexdec(substr($chrs, ($c + 4), 2)));
$utf8 .= $this->utf162utf8($utf16);
$c += 5;
break;
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
$utf8 .= $chrs{$c};
break;
case ($ord_chrs_c & 0xE0) == 0xC0:
// characters U-00000080 - U-000007FF, mask 110XXXXX
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 2);
++$c;
break;
case ($ord_chrs_c & 0xF0) == 0xE0:
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 3);
$c += 2;
break;
case ($ord_chrs_c & 0xF8) == 0xF0:
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 4);
$c += 3;
break;
case ($ord_chrs_c & 0xFC) == 0xF8:
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 5);
$c += 4;
break;
case ($ord_chrs_c & 0xFE) == 0xFC:
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 6);
$c += 5;
break;
}
}
return $utf8;
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
// array, or object notation
if ($str{0} == '[') {
$stk = array(SERVICES_JSON_IN_ARR);
$arr = array();
} else {
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$stk = array(SERVICES_JSON_IN_OBJ);
$obj = array();
} else {
$stk = array(SERVICES_JSON_IN_OBJ);
$obj = new stdClass();
}
}
array_push($stk, array('what' => SERVICES_JSON_SLICE,
'where' => 0,
'delim' => false));
$chrs = substr($str, 1, -1);
$chrs = $this->reduce_string($chrs);
if ($chrs == '') {
if (reset($stk) == SERVICES_JSON_IN_ARR) {
return $arr;
} else {
return $obj;
}
}
//print("\nparsing {$chrs}\n");
$strlen_chrs = strlen($chrs);
for ($c = 0; $c <= $strlen_chrs; ++$c) {
$top = end($stk);
$substr_chrs_c_2 = substr($chrs, $c, 2);
if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
// found a comma that is not inside a string, array, etc.,
// OR we've reached the end of the character list
$slice = substr($chrs, $top['where'], ($c - $top['where']));
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
if (reset($stk) == SERVICES_JSON_IN_ARR) {
// we are in an array, so just push an element onto the stack
array_push($arr, $this->decode($slice));
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
// we are in an object, so figure
// out the property name and set an
// element in an associative array,
// for now
$parts = array();
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// "name":value pair
$key = $this->decode($parts[1]);
$val = $this->decode($parts[2]);
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// name:value pair, where name is unquoted
$key = $parts[1];
$val = $this->decode($parts[2]);
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
}
}
} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
// found a quote, and we are not inside a string
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
//print("Found start of string at {$c}\n");
} elseif (($chrs{$c} == $top['delim']) &&
($top['what'] == SERVICES_JSON_IN_STR) &&
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
// found a quote, we're in a string, and it's not escaped
// we know that it's not escaped becase there is _not_ an
// odd number of backslashes at the end of the string so far
array_pop($stk);
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
} elseif (($chrs{$c} == '[') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-bracket, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
//print("Found start of array at {$c}\n");
} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
// found a right-bracket, and we're in an array
array_pop($stk);
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} elseif (($chrs{$c} == '{') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-brace, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
//print("Found start of object at {$c}\n");
} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
// found a right-brace, and we're in an object
array_pop($stk);
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} elseif (($substr_chrs_c_2 == '/*') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a comment start, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
$c++;
//print("Found start of comment at {$c}\n");
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
// found a comment end, and we're in one now
array_pop($stk);
$c++;
for ($i = $top['where']; $i <= $c; ++$i)
$chrs = substr_replace($chrs, ' ', $i, 1);
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
}
}
if (reset($stk) == SERVICES_JSON_IN_ARR) {
return $arr;
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
return $obj;
}
}
}
}
/**
* @todo Ultimately, this should just call PEAR::isError()
*/
function isError($data, $code = null)
{
if (class_exists('pear')) {
return PEAR::isError($data, $code);
} elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
is_subclass_of($data, 'services_json_error'))) {
return true;
}
return false;
}
}
if (class_exists('PEAR_Error')) {
class Services_JSON_Error extends PEAR_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
}
}
} else {
/**
* @todo Ultimately, this class shall be descended from PEAR_Error
*/
class Services_JSON_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
}
}
}
?>

21
thirdparty/json/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

521
thirdparty/json/Test-JSON.php vendored Normal file
View File

@ -0,0 +1,521 @@
<?php
// $Id: Test-JSON.php,v 1.28 2006/06/28 05:54:17 migurski Exp $
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Unit tests for Services_JSON.
* @see JSON.php
*
* @category
* @package Services_JSON
* @author Michal Migurski <mike-json@teczno.com>
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski
* @version CVS: $Id: Test-JSON.php,v 1.28 2006/06/28 05:54:17 migurski Exp $
* @license http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/
error_reporting(E_ALL);
require_once 'PHPUnit.php';
require_once 'JSON.php';
class Services_JSON_EncDec_TestCase extends PHPUnit_TestCase {
function Services_JSON_EncDec_TestCase($name) {
$this->PHPUnit_TestCase($name);
}
function setUp() {
$this->json = new Services_JSON();
$obj = new stdClass();
$obj->a_string = '"he":llo}:{world';
$obj->an_array = array(1, 2, 3);
$obj->obj = new stdClass();
$obj->obj->a_number = 123;
$this->obj = $obj;
$this->obj_j = '{"a_string":"\"he\":llo}:{world","an_array":[1,2,3],"obj":{"a_number":123}}';
$this->obj_d = 'object with properties, nested object and arrays';
$this->arr = array(null, true, array(1, 2, 3), "hello\"],[world!");
$this->arr_j = '[null,true,[1,2,3],"hello\"],[world!"]';
$this->arr_d = 'array with elements and nested arrays';
$this->str1 = 'hello world';
$this->str1_j = '"hello world"';
$this->str1_j_ = "'hello world'";
$this->str1_d = 'hello world';
$this->str1_d_ = 'hello world, double quotes';
$this->str2 = "hello\t\"world\"";
$this->str2_j = '"hello\\t\\"world\\""';
$this->str2_d = 'hello world, with tab, double-quotes';
$this->str3 = "\\\r\n\t\"/";
$this->str3_j = '"\\\\\\r\\n\\t\\"\\/"';
$this->str3_d = 'backslash, return, newline, tab, double-quote';
$this->str4 = 'héllö wørłd';
$this->str4_j = '"h\u00e9ll\u00f6 w\u00f8r\u0142d"';
$this->str4_j_ = '"héllö wørłd"';
$this->str4_d = 'hello world, with unicode';
}
function test_to_JSON()
{
$this->assertEquals('null', $this->json->encode(null), 'type case: null');
$this->assertEquals('true', $this->json->encode(true), 'type case: boolean true');
$this->assertEquals('false', $this->json->encode(false), 'type case: boolean false');
$this->assertEquals('1', $this->json->encode(1), 'numeric case: 1');
$this->assertEquals('-1', $this->json->encode(-1), 'numeric case: -1');
$this->assertEquals('1.000000', $this->json->encode(1.0), 'numeric case: 1.0');
$this->assertEquals('1.100000', $this->json->encode(1.1), 'numeric case: 1.1');
$this->assertEquals($this->str1_j, $this->json->encode($this->str1), "string case: {$this->str1_d}");
$this->assertEquals($this->str2_j, $this->json->encode($this->str2), "string case: {$this->str2_d}");
$this->assertEquals($this->str3_j, $this->json->encode($this->str3), "string case: {$this->str3_d}");
$this->assertEquals($this->str4_j, $this->json->encode($this->str4), "string case: {$this->str4_d}");
$this->assertEquals($this->arr_j, $this->json->encode($this->arr), "array case: {$this->arr_d}");
$this->assertEquals($this->obj_j, $this->json->encode($this->obj), "object case: {$this->obj_d}");
}
function test_from_JSON()
{
$this->assertEquals(null, $this->json->decode('null'), 'type case: null');
$this->assertEquals(true, $this->json->decode('true'), 'type case: boolean true');
$this->assertEquals(false, $this->json->decode('false'), 'type case: boolean false');
$this->assertEquals(1, $this->json->decode('1'), 'numeric case: 1');
$this->assertEquals(-1, $this->json->decode('-1'), 'numeric case: -1');
$this->assertEquals(1.0, $this->json->decode('1.0'), 'numeric case: 1.0');
$this->assertEquals(1.1, $this->json->decode('1.1'), 'numeric case: 1.1');
$this->assertEquals(11.0, $this->json->decode('1.1e1'), 'numeric case: 1.1e1');
$this->assertEquals(11.0, $this->json->decode('1.10e+1'), 'numeric case: 1.10e+1');
$this->assertEquals(0.11, $this->json->decode('1.1e-1'), 'numeric case: 1.1e-1');
$this->assertEquals(-0.11, $this->json->decode('-1.1e-1'), 'numeric case: -1.1e-1');
$this->assertEquals($this->str1, $this->json->decode($this->str1_j), "string case: {$this->str1_d}");
$this->assertEquals($this->str1, $this->json->decode($this->str1_j_), "string case: {$this->str1_d_}");
$this->assertEquals($this->str2, $this->json->decode($this->str2_j), "string case: {$this->str2_d}");
$this->assertEquals($this->str3, $this->json->decode($this->str3_j), "string case: {$this->str3_d}");
$this->assertEquals($this->str4, $this->json->decode($this->str4_j), "string case: {$this->str4_d}");
$this->assertEquals($this->str4, $this->json->decode($this->str4_j_), "string case: {$this->str4_d}");
$this->assertEquals($this->arr, $this->json->decode($this->arr_j), "array case: {$this->arr_d}");
$this->assertEquals($this->obj, $this->json->decode($this->obj_j), "object case: {$this->obj_d}");
}
function test_to_then_from_JSON()
{
$this->assertEquals(null, $this->json->decode($this->json->encode(null)), 'type case: null');
$this->assertEquals(true, $this->json->decode($this->json->encode(true)), 'type case: boolean true');
$this->assertEquals(false, $this->json->decode($this->json->encode(false)), 'type case: boolean false');
$this->assertEquals(1, $this->json->decode($this->json->encode(1)), 'numeric case: 1');
$this->assertEquals(-1, $this->json->decode($this->json->encode(-1)), 'numeric case: -1');
$this->assertEquals(1.0, $this->json->decode($this->json->encode(1.0)), 'numeric case: 1.0');
$this->assertEquals(1.1, $this->json->decode($this->json->encode(1.1)), 'numeric case: 1.1');
$this->assertEquals($this->str1, $this->json->decode($this->json->encode($this->str1)), "string case: {$this->str1_d}");
$this->assertEquals($this->str2, $this->json->decode($this->json->encode($this->str2)), "string case: {$this->str2_d}");
$this->assertEquals($this->str3, $this->json->decode($this->json->encode($this->str3)), "string case: {$this->str3_d}");
$this->assertEquals($this->str4, $this->json->decode($this->json->encode($this->str4)), "string case: {$this->str4_d}");
$this->assertEquals($this->arr, $this->json->decode($this->json->encode($this->arr)), "array case: {$this->arr_d}");
$this->assertEquals($this->obj, $this->json->decode($this->json->encode($this->obj)), "object case: {$this->obj_d}");
}
function test_from_then_to_JSON()
{
$this->assertEquals('null', $this->json->encode($this->json->decode('null')), 'type case: null');
$this->assertEquals('true', $this->json->encode($this->json->decode('true')), 'type case: boolean true');
$this->assertEquals('false', $this->json->encode($this->json->decode('false')), 'type case: boolean false');
$this->assertEquals('1', $this->json->encode($this->json->decode('1')), 'numeric case: 1');
$this->assertEquals('-1', $this->json->encode($this->json->decode('-1')), 'numeric case: -1');
$this->assertEquals('1.0', $this->json->encode($this->json->decode('1.0')), 'numeric case: 1.0');
$this->assertEquals('1.1', $this->json->encode($this->json->decode('1.1')), 'numeric case: 1.1');
$this->assertEquals($this->str1_j, $this->json->encode($this->json->decode($this->str1_j)), "string case: {$this->str1_d}");
$this->assertEquals($this->str2_j, $this->json->encode($this->json->decode($this->str2_j)), "string case: {$this->str2_d}");
$this->assertEquals($this->str3_j, $this->json->encode($this->json->decode($this->str3_j)), "string case: {$this->str3_d}");
$this->assertEquals($this->str4_j, $this->json->encode($this->json->decode($this->str4_j)), "string case: {$this->str4_d}");
$this->assertEquals($this->str4_j, $this->json->encode($this->json->decode($this->str4_j_)), "string case: {$this->str4_d}");
$this->assertEquals($this->arr_j, $this->json->encode($this->json->decode($this->arr_j)), "array case: {$this->arr_d}");
$this->assertEquals($this->obj_j, $this->json->encode($this->json->decode($this->obj_j)), "object case: {$this->obj_d}");
}
}
class Services_JSON_AssocArray_TestCase extends PHPUnit_TestCase {
function Services_JSON_AssocArray_TestCase($name) {
$this->PHPUnit_TestCase($name);
}
function setUp() {
$this->json_l = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
$this->json_s = new Services_JSON();
$this->arr = array('car1'=> array('color'=> 'tan', 'model' => 'sedan'),
'car2' => array('color' => 'red', 'model' => 'sports'));
$this->arr_jo = '{"car1":{"color":"tan","model":"sedan"},"car2":{"color":"red","model":"sports"}}';
$this->arr_d = 'associative array with nested associative arrays';
$this->arn = array(0=> array(0=> 'tan\\', 'model\\' => 'sedan'), 1 => array(0 => 'red', 'model' => 'sports'));
$this->arn_ja = '[{"0":"tan\\\\","model\\\\":"sedan"},{"0":"red","model":"sports"}]';
$this->arn_d = 'associative array with nested associative arrays, and some numeric keys thrown in';
$this->arrs = array (1 => 'one', 2 => 'two', 5 => 'five');
$this->arrs_jo = '{"1":"one","2":"two","5":"five"}';
$this->arrs_d = 'associative array numeric keys which are not fully populated in a range of 0 to length-1';
}
function test_type()
{
$this->assertEquals('array', gettype($this->json_l->decode($this->arn_ja)), "loose type should be array");
$this->assertEquals('array', gettype($this->json_s->decode($this->arn_ja)), "strict type should be array");
}
function test_to_JSON()
{
// both strict and loose JSON should result in an object
$this->assertEquals($this->arr_jo, $this->json_l->encode($this->arr), "array case - loose: {$this->arr_d}");
$this->assertEquals($this->arr_jo, $this->json_s->encode($this->arr), "array case - strict: {$this->arr_d}");
// ...unless the input array has some numeric indeces, in which case the behavior is to degrade to a regular array
$this->assertEquals($this->arn_ja, $this->json_s->encode($this->arn), "array case - strict: {$this->arn_d}");
// Test a sparsely populated numerically indexed associative array
$this->assertEquals($this->arrs_jo, $this->json_l->encode($this->arrs), "sparse numeric assoc array: {$this->arrs_d}");
}
function test_to_then_from_JSON()
{
// these tests motivated by a bug in which strings that end
// with backslashes followed by quotes were incorrectly decoded.
foreach(array('\\"', '\\\\"', '\\"\\"', '\\""\\""', '\\\\"\\\\"') as $v) {
$this->assertEquals(array($v), $this->json_l->decode($this->json_l->encode(array($v))));
$this->assertEquals(array('a' => $v), $this->json_l->decode($this->json_l->encode(array('a' => $v))));
}
}
}
class Services_JSON_NestedArray_TestCase extends PHPUnit_TestCase {
function Services_JSON_NestedArray_TestCase($name) {
$this->PHPUnit_TestCase($name);
}
function setUp() {
$this->json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
$this->str1 = '[{"this":"that"}]';
$this->arr1 = array(array('this' => 'that'));
$this->str2 = '{"this":["that"]}';
$this->arr2 = array('this' => array('that'));
$this->str3 = '{"params":[{"foo":["1"],"bar":"1"}]}';
$this->arr3 = array('params' => array(array('foo' => array('1'), 'bar' => '1')));
$this->str4 = '{"0": {"foo": "bar", "baz": "winkle"}}';
$this->arr4 = array('0' => array('foo' => 'bar', 'baz' => 'winkle'));
$this->str5 = '{"params":[{"options": {"old": [ ], "new": {"0": {"elements": {"old": [], "new": {"0": {"elementName": "aa", "isDefault": false, "elementRank": "0", "priceAdjust": "0", "partNumber": ""}}}, "optionName": "aa", "isRequired": false, "optionDesc": null}}}}]}';
$this->arr5 = array (
'params' => array (
0 => array (
'options' =>
array (
'old' => array(),
'new' => array (
0 => array (
'elements' => array (
'old' => array(),
'new' => array (
0 => array (
'elementName' => 'aa',
'isDefault' => false,
'elementRank' => '0',
'priceAdjust' => '0',
'partNumber' => '',
),
),
),
'optionName' => 'aa',
'isRequired' => false,
'optionDesc' => NULL,
),
),
),
),
),
);
}
function test_type()
{
$this->assertEquals('array', gettype($this->json->decode($this->str1)), "loose type should be array");
$this->assertEquals('array', gettype($this->json->decode($this->str2)), "loose type should be array");
$this->assertEquals('array', gettype($this->json->decode($this->str3)), "loose type should be array");
}
function test_from_JSON()
{
$this->assertEquals($this->arr1, $this->json->decode($this->str1), "simple compactly-nested array");
$this->assertEquals($this->arr2, $this->json->decode($this->str2), "simple compactly-nested array");
$this->assertEquals($this->arr3, $this->json->decode($this->str3), "complex compactly nested array");
$this->assertEquals($this->arr4, $this->json->decode($this->str4), "complex compactly nested array");
$this->assertEquals($this->arr5, $this->json->decode($this->str5), "super complex compactly nested array");
}
function _test_from_JSON()
{
$super = '{"params":[{"options": {"old": {}, "new": {"0": {"elements": {"old": {}, "new": {"0": {"elementName": "aa", "isDefault": false, "elementRank": "0", "priceAdjust": "0", "partNumber": ""}}}, "optionName": "aa", "isRequired": false, "optionDesc": ""}}}}]}';
print("trying {$super}...\n");
print var_export($this->json->decode($super));
}
}
class Services_JSON_Object_TestCase extends PHPUnit_TestCase {
function Services_JSON_Object_TestCase($name) {
$this->PHPUnit_TestCase($name);
}
function setUp() {
$this->json_l = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
$this->json_s = new Services_JSON();
$this->obj_j = '{"a_string":"\"he\":llo}:{world","an_array":[1,2,3],"obj":{"a_number":123}}';
$this->obj1->car1->color = 'tan';
$this->obj1->car1->model = 'sedan';
$this->obj1->car2->color = 'red';
$this->obj1->car2->model = 'sports';
$this->obj1_j = '{"car1":{"color":"tan","model":"sedan"},"car2":{"color":"red","model":"sports"}}';
$this->obj1_d = 'Object with nested objects';
}
function test_type()
{
$this->assertEquals('object', gettype($this->json_s->decode($this->obj_j)), "checking whether decoded type is object");
$this->assertEquals('array', gettype($this->json_l->decode($this->obj_j)), "checking whether decoded type is array");
}
function test_to_JSON()
{
$this->assertEquals($this->obj1_j, $this->json_s->encode($this->obj1), "object - strict: {$this->obj1_d}");
$this->assertEquals($this->obj1_j, $this->json_l->encode($this->obj1), "object - loose: {$this->obj1_d}");
}
function test_from_then_to_JSON()
{
$this->assertEquals($this->obj_j, $this->json_s->encode($this->json_s->decode($this->obj_j)), "object case");
$this->assertEquals($this->obj_j, $this->json_l->encode($this->json_l->decode($this->obj_j)), "array case");
}
}
class Services_JSON_Spaces_Comments_TestCase extends PHPUnit_TestCase {
function Services_JSON_Spaces_Comments_TestCase($name) {
$this->PHPUnit_TestCase($name);
}
function setUp() {
$this->json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
$this->obj_j = '{"a_string":"\"he\":llo}:{world","an_array":[1,2,3],"obj":{"a_number":123}}';
$this->obj_js = '{"a_string": "\"he\":llo}:{world",
"an_array":[1, 2, 3],
"obj": {"a_number":123}}';
$this->obj_jc1 = '{"a_string": "\"he\":llo}:{world",
// here is a comment, hoorah
"an_array":[1, 2, 3],
"obj": {"a_number":123}}';
$this->obj_jc2 = '/* this here is the sneetch */ "the sneetch"
// this has been the sneetch.';
$this->obj_jc3 = '{"a_string": "\"he\":llo}:{world",
/* here is a comment, hoorah */
"an_array":[1, 2, 3 /* and here is another */],
"obj": {"a_number":123}}';
$this->obj_jc4 = '{\'a_string\': "\"he\":llo}:{world",
/* here is a comment, hoorah */
\'an_array\':[1, 2, 3 /* and here is another */],
"obj": {"a_number":123}}';
}
function test_spaces()
{
$this->assertEquals($this->json->decode($this->obj_j), $this->json->decode($this->obj_js), "checking whether notation with spaces works");
}
function test_comments()
{
$this->assertEquals($this->json->decode($this->obj_j), $this->json->decode($this->obj_jc1), "checking whether notation with single line comments works");
$this->assertEquals('the sneetch', $this->json->decode($this->obj_jc2), "checking whether notation with multiline comments works");
$this->assertEquals($this->json->decode($this->obj_j), $this->json->decode($this->obj_jc3), "checking whether notation with multiline comments works");
$this->assertEquals($this->json->decode($this->obj_j), $this->json->decode($this->obj_jc4), "checking whether notation with single-quotes and multiline comments works");
}
}
class Services_JSON_Empties_TestCase extends PHPUnit_TestCase {
function Services_JSON_Empties_TestCase($name) {
$this->PHPUnit_TestCase($name);
}
function setUp() {
$this->json_l = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
$this->json_s = new Services_JSON();
$this->obj0_j = '{}';
$this->arr0_j = '[]';
$this->obj1_j = '{ }';
$this->arr1_j = '[ ]';
$this->obj2_j = '{ /* comment inside */ }';
$this->arr2_j = '[ /* comment inside */ ]';
}
function test_type()
{
$this->assertEquals('array', gettype($this->json_l->decode($this->arr0_j)), "should be array");
$this->assertEquals('object', gettype($this->json_s->decode($this->obj0_j)), "should be object");
$this->assertEquals(0, count($this->json_l->decode($this->arr0_j)), "should be empty array");
$this->assertEquals(0, count(get_object_vars($this->json_s->decode($this->obj0_j))), "should be empty object");
$this->assertEquals('array', gettype($this->json_l->decode($this->arr1_j)), "should be array, even with space");
$this->assertEquals('object', gettype($this->json_s->decode($this->obj1_j)), "should be object, even with space");
$this->assertEquals(0, count($this->json_l->decode($this->arr1_j)), "should be empty array, even with space");
$this->assertEquals(0, count(get_object_vars($this->json_s->decode($this->obj1_j))), "should be empty object, even with space");
$this->assertEquals('array', gettype($this->json_l->decode($this->arr2_j)), "should be array, despite comment");
$this->assertEquals('object', gettype($this->json_s->decode($this->obj2_j)), "should be object, despite comment");
$this->assertEquals(0, count($this->json_l->decode($this->arr2_j)), "should be empty array, despite comment");
$this->assertEquals(0, count(get_object_vars($this->json_s->decode($this->obj2_j))), "should be empty object, despite commentt");
}
}
class Services_JSON_UnquotedKeys_TestCase extends PHPUnit_TestCase {
function Services_JSON_UnquotedKeys_TestCase($name) {
$this->PHPUnit_TestCase($name);
}
function setUp() {
$this->json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
$this->arn = array(0=> array(0=> 'tan', 'model' => 'sedan'), 1 => array(0 => 'red', 'model' => 'sports'));
$this->arn_ja = '[{0:"tan","model":"sedan"},{"0":"red",model:"sports"}]';
$this->arn_d = 'associative array with unquoted keys, nested associative arrays, and some numeric keys thrown in';
$this->arrs = array (1 => 'one', 2 => 'two', 5 => 'fi"ve');
$this->arrs_jo = '{"1":"one",2:"two","5":\'fi"ve\'}';
$this->arrs_d = 'associative array with unquoted keys, single-quoted values, numeric keys which are not fully populated in a range of 0 to length-1';
}
function test_from_JSON()
{
// ...unless the input array has some numeric indeces, in which case the behavior is to degrade to a regular array
$this->assertEquals($this->arn, $this->json->decode($this->arn_ja), "array case - strict: {$this->arn_d}");
// Test a sparsely populated numerically indexed associative array
$this->assertEquals($this->arrs, $this->json->decode($this->arrs_jo), "sparse numeric assoc array: {$this->arrs_d}");
}
}
class Services_JSON_ErrorSuppression_TestCase extends PHPUnit_TestCase {
function Services_JSON_ErrorSuppression_TestCase($name) {
$this->PHPUnit_TestCase($name);
}
function setUp() {
$this->json = new Services_JSON();
$this->json_ = new Services_JSON(SERVICES_JSON_SUPPRESS_ERRORS);
$this->res = tmpfile();
$this->res_j_ = 'null';
$this->res_d = 'naked resource';
$this->arr = array('a', 1, tmpfile());
$this->arr_j_ = '["a",1,null]';
$this->arr_d = 'array with string, number and resource';
$obj = new stdClass();
$obj->a_string = '"he":llo}:{world';
$obj->an_array = array(1, 2, 3);
$obj->resource = tmpfile();
$this->obj = $obj;
$this->obj_j_ = '{"a_string":"\"he\":llo}:{world","an_array":[1,2,3],"resource":null}';
$this->obj_d = 'object with properties, array, and nested resource';
}
function test_to_JSON()
{
$this->assertTrue(Services_JSON::isError($this->json->encode($this->res)), "resource case: {$this->res_d}");
$this->assertTrue(Services_JSON::isError($this->json->encode($this->arr)), "array case: {$this->arr_d}");
$this->assertTrue(Services_JSON::isError($this->json->encode($this->obj)), "object case: {$this->obj_d}");
}
function test_to_JSON_suppressed()
{
$this->assertEquals($this->res_j_, $this->json_->encode($this->res), "resource case: {$this->res_d}");
$this->assertEquals($this->arr_j_, $this->json_->encode($this->arr), "array case: {$this->arr_d}");
$this->assertEquals($this->obj_j_, $this->json_->encode($this->obj), "object case: {$this->obj_d}");
}
}
$suite = new PHPUnit_TestSuite('Services_JSON_EncDec_TestCase');
$result = PHPUnit::run($suite);
echo $result->toString();
$suite = new PHPUnit_TestSuite('Services_JSON_AssocArray_TestCase');
$result = PHPUnit::run($suite);
echo $result->toString();
$suite = new PHPUnit_TestSuite('Services_JSON_NestedArray_TestCase');
$result = PHPUnit::run($suite);
echo $result->toString();
$suite = new PHPUnit_TestSuite('Services_JSON_Object_TestCase');
$result = PHPUnit::run($suite);
echo $result->toString();
$suite = new PHPUnit_TestSuite('Services_JSON_Spaces_Comments_TestCase');
$result = PHPUnit::run($suite);
echo $result->toString();
$suite = new PHPUnit_TestSuite('Services_JSON_Empties_TestCase');
$result = PHPUnit::run($suite);
echo $result->toString();
$suite = new PHPUnit_TestSuite('Services_JSON_UnquotedKeys_TestCase');
$result = PHPUnit::run($suite);
echo $result->toString();
$suite = new PHPUnit_TestSuite('Services_JSON_ErrorSuppression_TestCase');
$result = PHPUnit::run($suite);
echo $result->toString();
?>

26
thirdparty/simplepie/LICENSE.txt vendored Normal file
View File

@ -0,0 +1,26 @@
Copyright (c) 2004-2007, Ryan Parman and Geoffrey Sneddon.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of the SimplePie Team nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

30
thirdparty/simplepie/README.txt vendored Normal file
View File

@ -0,0 +1,30 @@
SIMPLEPIE
http://simplepie.org
By Ryan Parman and Geoffrey Sneddon
BSD-LICENSED
http://www.opensource.org/licenses/bsd-license.php
WHAT COMES IN THE PACKAGE?
1) simplepie.inc - The SimplePie library. This is all that's required for your pages.
2) README.txt - This document.
3) LICENSE.txt - A copy of the BSD license.
4) compatibility_test - The SimplePie compatibility test that checks your server for required settings.
5) demo - A basic feed reader demo that shows off some of SimplePie's more noticable features.
6) idn - A third-party library that SimplePie can optionally use to understand Internationalized Domain Names (IDNs).
7) test - SimplePie's unit test suite. This is only available in SVN builds.
TO START THE DEMO:
1) Upload this package to your webserver.
2) Make sure that the cache folder inside of the demo folder is server-writable.
3) Navigate your browser to the demo folder.
SUPPORT:
For further setup and install documentation, function references, etc., visit:
http://simplepie.org/wiki/
For bug reports, feature requests and other support, visit:
http://simplepie.org/support/
For more insight on SimplePie development, visit:
http://simplepie.org/development/

5413
thirdparty/simplepie/SimplePie.php vendored Executable file
View File

@ -0,0 +1,5413 @@
<?php
/**
* A PHP-Based RSS and Atom Feed Framework
* Takes the hard work out of managing a complete RSS/Atom solution.
* Version: "Lemon Meringue"
* Updated: 3 November 2006
* Copyright: 2004-2006 Ryan Parman, Geoffrey Sneddon
* http://simplepie.org
* LICENSE: GNU Lesser General Public License 2.1 (LGPL)
* Please submit all bug reports and feature requests to the SimplePie forums. http://simplepie.org/support/
* @package sapphire
* @subpackage integration
*/
class SimplePie
{
// SimplePie Info
var $name = 'SimplePie';
var $version = '1.0 b3.1';
var $build = '20061103';
var $url = 'http://simplepie.org/';
var $useragent;
var $linkback;
// Other objects, instances created here so we can set options on them
var $sanitize;
// Options
var $rss_url;
var $file;
var $timeout = 10;
var $xml_dump = false;
var $enable_cache = true;
var $max_minutes = 60;
var $cache_location = './cache';
var $order_by_date = true;
var $input_encoding = false;
var $cache_class = 'SimplePie_Cache';
var $locator_class = 'SimplePie_Locator';
var $parser_class = 'SimplePie_Parser';
var $file_class = 'SimplePie_File';
var $force_fsockopen = false;
var $cache_name_type = 'sha1';
// Misc. variables
var $data;
var $error;
function SimplePie($feed_url = null, $cache_location = null, $cache_max_minutes = null)
{
// Couple of variables built up from other variables
$this->useragent = $this->name . '/' . $this->version . ' (Feed Parser; ' . $this->url . '; Allow like Gecko) Build/' . $this->build;
$this->linkback = '<a href="' . $this->url . '" title="' . $this->name . ' ' . $this->version . '">' . $this->name . '</a>';
// Other objects, instances created here so we can set options on them
$this->sanitize = new SimplePie_Sanitize;
// Set options if they're passed to the constructor
if (!is_null($feed_url))
{
$this->feed_url($feed_url);
}
if (!is_null($cache_location))
{
$this->cache_location($cache_location);
}
if (!is_null($cache_max_minutes))
{
$this->cache_max_minutes($cache_max_minutes);
}
// If we've passed an xmldump variable in the URL, snap into XMLdump mode
if (isset($_GET['xmldump']))
{
$this->enable_xmldump(true);
}
// Only init the script if we're passed a feed URL
if (!is_null($feed_url))
{
return $this->init();
}
}
function feed_url($url)
{
$this->rss_url = SimplePie_Misc::fix_protocol($url, 1);
}
function set_file(&$file)
{
if (is_a($file, 'SimplePie_File'))
{
$this->rss_url = $file->url;
$this->file =& $file;
}
}
function set_timeout($timeout = 10)
{
$this->timeout = (int) $timeout;
}
function set_raw_data($data)
{
$this->raw_data = trim((string) $data);
}
function enable_xmldump($enable = false)
{
$this->xml_dump = (bool) $enable;
}
function enable_caching($enable = true)
{
$this->enable_cache = (bool) $enable;
}
function cache_max_minutes($minutes = 60)
{
$this->max_minutes = (float) $minutes;
}
function cache_location($location = './cache')
{
$this->cache_location = (string) $location;
}
function order_by_date($enable = true)
{
$this->order_by_date = (bool) $enable;
}
function input_encoding($encoding = false)
{
if ($encoding)
{
$this->input_encoding = (string) $encoding;
}
else
{
$this->input_encoding = false;
}
}
function set_cache_class($class = 'SimplePie_Cache')
{
if (SimplePie_Misc::is_a_class($class, 'SimplePie_Cache'))
{
$this->cache_class = $class;
return true;
}
return false;
}
function set_locator_class($class = 'SimplePie_Locator')
{
if (SimplePie_Misc::is_a_class($class, 'SimplePie_Locator'))
{
$this->locator_class = $class;
return true;
}
return false;
}
function set_parser_class($class = 'SimplePie_Parser')
{
if (SimplePie_Misc::is_a_class($class, 'SimplePie_Parser'))
{
$this->parser_class = $class;
return true;
}
return false;
}
function set_file_class($class = 'SimplePie_File')
{
if (SimplePie_Misc::is_a_class($class, 'SimplePie_File'))
{
$this->file_class = $class;
return true;
}
return false;
}
function set_sanitize_class($object = 'SimplePie_Sanitize')
{
if (class_exists($object))
{
$this->sanitize = new $object;
return true;
}
return false;
}
function set_useragent($ua)
{
$this->useragent = (string) $ua;
}
function force_fsockopen($enable = false)
{
$this->force_fsockopen = (bool) $enable;
}
function set_cache_name_type($type = 'sha1')
{
$type = strtolower(trim($type));
switch ($type)
{
case 'crc32':
$this->cache_name_type = 'crc32';
break;
case 'md5':
$this->cache_name_type = 'md5';
break;
case 'rawurlencode':
$this->cache_name_type = 'rawurlencode';
break;
case 'urlencode':
$this->cache_name_type = 'urlencode';
break;
default:
$this->cache_name_type = 'sha1';
break;
}
}
function bypass_image_hotlink($get = false)
{
$this->sanitize->bypass_image_hotlink($get);
}
function bypass_image_hotlink_page($page = false)
{
$this->sanitize->bypass_image_hotlink_page($page);
}
function replace_headers($enable = false)
{
$this->sanitize->replace_headers($enable);
}
function remove_div($enable = true)
{
$this->sanitize->remove_div($enable);
}
function strip_ads($enable = false)
{
$this->sanitize->strip_ads($enable);
}
function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'), $encode = null)
{
$this->sanitize->strip_htmltags($tags);
if (!is_null($encode))
{
$this->sanitize->encode_instead_of_strip($tags);
}
}
function encode_instead_of_strip($enable = true)
{
$this->sanitize->encode_instead_of_strip($enable);
}
function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur'))
{
$this->sanitize->strip_attributes($attribs);
}
function output_encoding($encoding = 'UTF-8')
{
$this->sanitize->output_encoding($encoding);
}
function set_item_class($class = 'SimplePie_Item')
{
return $this->sanitize->set_item_class($class);
}
function set_author_class($class = 'SimplePie_Author')
{
return $this->sanitize->set_author_class($class);
}
function set_enclosure_class($class = 'SimplePie_Enclosure')
{
return $this->sanitize->set_enclosure_class($class);
}
function init()
{
if (!(function_exists('version_compare') && ((version_compare(phpversion(), '4.3.2', '>=') && version_compare(phpversion(), '5', '<')) || version_compare(phpversion(), '5.0.3', '>='))) || !extension_loaded('xml') || !extension_loaded('pcre'))
{
return false;
}
if ($this->sanitize->bypass_image_hotlink && !empty($_GET[$this->sanitize->bypass_image_hotlink]))
{
if (get_magic_quotes_gpc())
{
$_GET[$this->sanitize->bypass_image_hotlink] = stripslashes($_GET[$this->sanitize->bypass_image_hotlink]);
}
SimplePie_Misc::display_file($_GET[$this->sanitize->bypass_image_hotlink], 10, $this->useragent);
}
if (isset($_GET['js']))
{
$embed = <<<EOT
function embed_odeo(link) {
document.writeln('<embed src="http://odeo.com/flash/audio_player_fullsize.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="440" height="80" wmode="transparent" allowScriptAccess="any" flashvars="valid_sample_rate=true&external_url='+link+'"></embed>');
}
function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) {
if (placeholder != '') {
document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
}
else {
document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
}
}
function embed_flash(bgcolor, width, height, link, loop, type) {
document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>');
}
function embed_wmedia(width, height, link) {
document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>');
}
EOT;
if (function_exists('ob_gzhandler'))
{
ob_start('ob_gzhandler');
}
header('Content-type: text/javascript; charset: UTF-8');
header('Cache-Control: must-revalidate');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 86400) . ' GMT');
echo $embed;
exit;
}
if (!empty($this->rss_url) || !empty($this->raw_data))
{
$this->data = array();
$cache = false;
if (!empty($this->rss_url))
{
// Decide whether to enable caching
if ($this->enable_cache && preg_match('/^http(s)?:\/\//i', $this->rss_url))
{
$cache = new $this->cache_class($this->cache_location, call_user_func($this->cache_name_type, $this->rss_url), 'spc');
}
// If it's enabled and we don't want an XML dump, use the cache
if ($cache && !$this->xml_dump)
{
// Load the Cache
$this->data = $cache->load();
if (!empty($this->data))
{
// If we've hit a collision just rerun it with caching disabled
if (isset($this->data['url']) && $this->data['url'] != $this->rss_url)
{
$cache = false;
}
// If we've got a feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL
else if (!empty($this->data['feed_url']))
{
if ($this->data['feed_url'] == $this->data['url'])
{
$cache->unlink();
}
else
{
$this->feed_url($this->data['feed_url']);
return $this->init();
}
}
// If the cache is new enough
else if ($cache->mtime() + $this->max_minutes * 60 < time())
{
// If we have last-modified and/or etag set
if (!empty($this->data['last-modified']) || !empty($this->data['etag']))
{
$headers = array();
if (!empty($this->data['last-modified']))
{
$headers['if-modified-since'] = $this->data['last-modified'];
}
if (!empty($this->data['etag']))
{
$headers['if-none-match'] = $this->data['etag'];
}
$file = new $this->file_class($this->rss_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen);
if ($file->success)
{
$headers = $file->headers();
if ($headers['status']['code'] == 304)
{
$cache->touch();
return true;
}
}
else
{
unset($file);
}
}
// If we don't have last-modified or etag set, just clear the cache
else
{
$cache->unlink();
}
}
// If the cache is still valid, just return true
else
{
return true;
}
}
// If the cache is empty, delete it
else
{
$cache->unlink();
}
}
$this->data = array();
// If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it.
if (!isset($file))
{
if (is_a($this->file, 'SimplePie_File') && $this->file->url == $this->rss_url)
{
$file =& $this->file;
}
else
{
$file = new $this->file_class($this->rss_url, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen);
}
}
// If the file connection has an error, set SimplePie::error to that and quit
if (!$file->success)
{
$this->error = $file->error;
return false;
}
// Check if the supplied URL is a feed, if it isn't, look for it.
$locate = new $this->locator_class($file, $this->timeout, $this->useragent);
if (!$locate->is_feed($file))
{
$feed = $locate->find();
if ($feed)
{
if ($cache && !$cache->save(array('url' => $this->rss_url, 'feed_url' => $feed)))
{
$this->error = "$cache->name is not writeable";
SimplePie_Misc::error($this->error, E_USER_WARNING, __FILE__, __LINE__);
}
$this->rss_url = $feed;
return $this->init();
}
else
{
$this->error = "A feed could not be found at $this->rss_url";
SimplePie_Misc::error($this->error, E_USER_WARNING, __FILE__, __LINE__);
return false;
}
}
$headers = $file->headers();
$data = trim($file->body());
$file->close();
unset($file);
}
else
{
$data = $this->raw_data;
}
// First check to see if input has been overridden.
if (!empty($this->input_encoding))
{
$encoding = $this->input_encoding;
}
// Second try HTTP headers
else if (!empty($headers['content-type']) && preg_match('/charset\s*=\s*([^;]*)/i', $headers['content-type'], $charset))
{
$encoding = $charset[1];
}
// Then prolog, if at the very start of the document
else if (preg_match('/^<\?xml(.*)?>/msiU', $data, $prolog) && preg_match('/encoding\s*=\s*("([^"]*)"|\'([^\']*)\')/Ui', $prolog[1], $encoding))
{
$encoding = substr($encoding[1], 1, -1);
}
// UTF-32 Big Endian BOM
else if (strpos($data, sprintf('%c%c%c%c', 0x00, 0x00, 0xFE, 0xFF)) === 0)
{
$encoding = 'UTF-32be';
}
// UTF-32 Little Endian BOM
else if (strpos($data, sprintf('%c%c%c%c', 0xFF, 0xFE, 0x00, 0x00)) === 0)
{
$encoding = 'UTF-32';
}
// UTF-16 Big Endian BOM
else if (strpos($data, sprintf('%c%c', 0xFE, 0xFF)) === 0)
{
$encoding = 'UTF-16be';
}
// UTF-16 Little Endian BOM
else if (strpos($data, sprintf('%c%c', 0xFF, 0xFE)) === 0)
{
$encoding = 'UTF-16le';
}
// UTF-8 BOM
else if (strpos($data, sprintf('%c%c%c', 0xEF, 0xBB, 0xBF)) === 0)
{
$encoding = 'UTF-8';
}
// Fallback to the default
else
{
$encoding = null;
}
// Change the encoding to UTF-8 (as we always use UTF-8 internally)
$data = SimplePie_Misc::change_encoding($data, $encoding, 'UTF-8');
// Start parsing
$data = new $this->parser_class($data, 'UTF-8', $this->xml_dump);
// If we want the XML, just output that and quit
if ($this->xml_dump)
{
header('Content-type: text/xml; charset=UTF-8');
echo $data->data;
exit;
}
// If it's parsed fine
else if (!$data->error_code)
{
// Parse the data, and make it sane
$this->sanitize->parse_data_array($data->data, $this->rss_url);
unset($data);
// Get the sane data
$this->data['feedinfo'] = $this->sanitize->feedinfo;
unset($this->sanitize->feedinfo);
$this->data['info'] = $this->sanitize->info;
unset($this->sanitize->info);
$this->data['items'] = $this->sanitize->items;
unset($this->sanitize->items);
$this->data['feedinfo']['encoding'] = $this->sanitize->output_encoding;
$this->data['url'] = $this->rss_url;
// Store the headers that we need
if (!empty($headers['last-modified']))
{
$this->data['last-modified'] = $headers['last-modified'];
}
if (!empty($headers['etag']))
{
$this->data['etag'] = $headers['etag'];
}
// If we want to order it by date, check if all items have a date, and then sort it
if ($this->order_by_date && !empty($this->data['items']))
{
$do_sort = true;
foreach ($this->data['items'] as $item)
{
if (!$item->get_date('U'))
{
$do_sort = false;
break;
}
}
if ($do_sort)
{
usort($this->data['items'], create_function('$a, $b', 'if ($a->get_date(\'U\') == $b->get_date(\'U\')) return 1; return ($a->get_date(\'U\') < $b->get_date(\'U\')) ? 1 : -1;'));
}
}
// Cache the file if caching is enabled
if ($cache && !$cache->save($this->data))
{
$this->error = "$cache->name is not writeable";
SimplePie_Misc::error($this->error, E_USER_WARNING, __FILE__, __LINE__);
}
return true;
}
// If we have an error, just set SimplePie::error to it and quit
else
{
$this->error = "XML error: $data->error_string at line $data->current_line, column $data->current_column";
SimplePie_Misc::error($this->error, E_USER_WARNING, __FILE__, __LINE__);
return false;
}
}
}
function get_encoding()
{
if (!empty($this->data['feedinfo']['encoding']))
{
return $this->data['feedinfo']['encoding'];
}
else
{
return false;
}
}
function handle_content_type($mime = 'text/html')
{
if (!headers_sent())
{
$header = "Content-type: $mime;";
if ($this->get_encoding())
{
$header .= ' charset=' . $this->get_encoding();
}
else
{
$header .= ' charset=UTF-8';
}
header($header);
}
}
function get_type()
{
if (!empty($this->data['feedinfo']['type']))
{
return $this->data['feedinfo']['type'];
}
else
{
return false;
}
}
function get_version()
{
if (!empty($this->data['feedinfo']['version']))
{
return $this->data['feedinfo']['version'];
}
else
{
return false;
}
}
function get_favicon($check = false, $alternate = null)
{
if (!empty($this->data['info']['link']['alternate'][0]))
{
$favicon = SimplePie_Misc::absolutize_url('/favicon.ico', $this->get_feed_link());
if ($check)
{
$file = new $this->file_class($favicon, $this->timeout/10, 5, null, $this->useragent, $this->force_fsockopen);
$headers = $file->headers();
$file->close();
if ($headers['status']['code'] == 200)
{
return $favicon;
}
}
else
{
return $favicon;
}
}
if (!is_null($alternate))
{
return $alternate;
}
else
{
return false;
}
}
function subscribe_url()
{
if (!empty($this->rss_url))
{
return $this->rss_url;
}
else
{
return false;
}
}
function subscribe_feed()
{
if (!empty($this->rss_url))
{
return SimplePie_Misc::fix_protocol($this->rss_url, 2);
}
else
{
return false;
}
}
function subscribe_outlook()
{
if (!empty($this->rss_url))
{
return 'outlook' . SimplePie_Misc::fix_protocol($this->rss_url, 2);
}
else
{
return false;
}
}
function subscribe_podcast()
{
if (!empty($this->rss_url))
{
return SimplePie_Misc::fix_protocol($this->rss_url, 3);
}
else
{
return false;
}
}
function subscribe_aol()
{
if ($this->subscribe_url())
{
return 'http://feeds.my.aol.com/add.jsp?url=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_bloglines()
{
if ($this->subscribe_url())
{
return 'http://www.bloglines.com/sub/' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_eskobo()
{
if ($this->subscribe_url())
{
return 'http://www.eskobo.com/?AddToMyPage=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_feedfeeds()
{
if ($this->subscribe_url())
{
return 'http://www.feedfeeds.com/add?feed=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_feedlounge()
{
if ($this->subscribe_url())
{
return 'http://my.feedlounge.com/external/subscribe?url=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_feedster()
{
if ($this->subscribe_url())
{
return 'http://www.feedster.com/myfeedster.php?action=addrss&amp;confirm=no&amp;rssurl=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_google()
{
if ($this->subscribe_url())
{
return 'http://fusion.google.com/add?feedurl=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_gritwire()
{
if ($this->subscribe_url())
{
return 'http://my.gritwire.com/feeds/addExternalFeed.aspx?FeedUrl=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_msn()
{
if ($this->subscribe_url())
{
$url = 'http://my.msn.com/addtomymsn.armx?id=rss&amp;ut=' . rawurlencode($this->subscribe_url());
if ($this->get_feed_link())
{
$url .= '&amp;ru=' . rawurlencode($this->get_feed_link());
}
return $url;
}
else
{
return false;
}
}
function subscribe_netvibes()
{
if ($this->subscribe_url())
{
return 'http://www.netvibes.com/subscribe.php?url=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_newsburst()
{
if ($this->subscribe_url())
{
return 'http://www.newsburst.com/Source/?add=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_newsgator()
{
if ($this->subscribe_url())
{
return 'http://www.newsgator.com/ngs/subscriber/subext.aspx?url=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_odeo()
{
if ($this->subscribe_url())
{
return 'http://www.odeo.com/listen/subscribe?feed=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_pluck()
{
if ($this->subscribe_url())
{
return 'http://client.pluck.com/pluckit/prompt.aspx?GCID=C12286x053&amp;a=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_podnova()
{
if ($this->subscribe_url())
{
return 'http://www.podnova.com/index_your_podcasts.srf?action=add&amp;url=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_rojo()
{
if ($this->subscribe_url())
{
return 'http://www.rojo.com/add-subscription?resource=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function subscribe_yahoo()
{
if ($this->subscribe_url())
{
return 'http://add.my.yahoo.com/rss?url=' . rawurlencode($this->subscribe_url());
}
else
{
return false;
}
}
function get_feed_title()
{
if (!empty($this->data['info']['title']))
{
return $this->data['info']['title'];
}
else
{
return false;
}
}
function get_feed_link()
{
if (!empty($this->data['info']['link']['alternate'][0]))
{
return $this->data['info']['link']['alternate'][0];
}
else
{
return false;
}
}
function get_feed_links()
{
if (!empty($this->data['info']['link']))
{
return $this->data['info']['link'];
}
else
{
return false;
}
}
function get_feed_description()
{
if (!empty($this->data['info']['description']))
{
return $this->data['info']['description'];
}
else if (!empty($this->data['info']['dc:description']))
{
return $this->data['info']['dc:description'];
}
else if (!empty($this->data['info']['tagline']))
{
return $this->data['info']['tagline'];
}
else if (!empty($this->data['info']['subtitle']))
{
return $this->data['info']['subtitle'];
}
else
{
return false;
}
}
function get_feed_copyright()
{
if (!empty($this->data['info']['copyright']))
{
return $this->data['info']['copyright'];
}
else
{
return false;
}
}
function get_feed_language()
{
if (!empty($this->data['info']['language']))
{
return $this->data['info']['language'];
}
else if (!empty($this->data['info']['xml:lang']))
{
return $this->data['info']['xml:lang'];
}
else
{
return false;
}
}
function get_image_exist()
{
if (!empty($this->data['info']['image']['url']) || !empty($this->data['info']['image']['logo']))
{
return true;
}
else
{
return false;
}
}
function get_image_title()
{
if (!empty($this->data['info']['image']['title']))
{
return $this->data['info']['image']['title'];
}
else
{
return false;
}
}
function get_image_url()
{
if (!empty($this->data['info']['image']['url']))
{
return $this->data['info']['image']['url'];
}
else if (!empty($this->data['info']['image']['logo']))
{
return $this->data['info']['image']['logo'];
}
else
{
return false;
}
}
function get_image_link()
{
if (!empty($this->data['info']['image']['link']))
{
return $this->data['info']['image']['link'];
}
else
{
return false;
}
}
function get_image_width()
{
if (!empty($this->data['info']['image']['width']))
{
return $this->data['info']['image']['width'];
}
else
{
return false;
}
}
function get_image_height()
{
if (!empty($this->data['info']['image']['height']))
{
return $this->data['info']['image']['height'];
}
else
{
return false;
}
}
function get_item_quantity($max = 0)
{
if (!empty($this->data['items']))
{
$qty = sizeof($this->data['items']);
}
else
{
$qty = 0;
}
if ($max == 0)
{
return $qty;
}
else
{
return ($qty > $max) ? $max : $qty;
}
}
function get_item($key = 0)
{
if (!empty($this->data['items'][$key]))
{
return $this->data['items'][$key];
}
else
{
return false;
}
}
function get_items($start = 0, $end = 0)
{
if ($this->get_item_quantity() > 0)
{
if ($end == 0)
{
return array_slice($this->data['items'], $start);
}
else
{
return array_slice($this->data['items'], $start, $end);
}
}
else
{
return false;
}
}
}
/**
* @package sapphire
* @subpackage integration
*/
class SimplePie_Item
{
var $data;
function SimplePie_Item($data)
{
$this->data =& $data;
}
function get_id()
{
if (!empty($this->data['guid']['data']))
{
return $this->data['guid']['data'];
}
else if (!empty($this->data['id']))
{
return $this->data['id'];
}
else
{
return false;
}
}
function get_title()
{
if (!empty($this->data['title']))
{
return $this->data['title'];
}
else if (!empty($this->data['dc:title']))
{
return $this->data['dc:title'];
}
else
{
return false;
}
}
function get_description()
{
if (!empty($this->data['content']))
{
return $this->data['content'];
}
else if (!empty($this->data['encoded']))
{
return $this->data['encoded'];
}
else if (!empty($this->data['summary']))
{
return $this->data['summary'];
}
else if (!empty($this->data['description']))
{
return $this->data['description'];
}
else if (!empty($this->data['dc:description']))
{
return $this->data['dc:description'];
}
else if (!empty($this->data['longdesc']))
{
return $this->data['longdesc'];
}
else
{
return false;
}
}
function get_category($key = 0)
{
$categories = $this->get_categories();
if (!empty($categories[$key]))
{
return $categories[$key];
}
else
{
return false;
}
}
function get_categories()
{
$categories = array();
if (!empty($this->data['category']))
{
$categories = array_merge($categories, $this->data['category']);
}
if (!empty($this->data['subject']))
{
$categories = array_merge($categories, $this->data['subject']);
}
if (!empty($this->data['term']))
{
$categories = array_merge($categories, $this->data['term']);
}
if (!empty($categories))
{
return array_unique($categories);
}
else
{
return false;
}
}
function get_author($key = 0)
{
$authors = $this->get_authors();
if (!empty($authors[$key]))
{
return $authors[$key];
}
else
{
return false;
}
}
function get_authors()
{
$authors = array();
if (!empty($this->data['author']))
{
$authors = array_merge($authors, $this->data['author']);
}
if (!empty($this->data['creator']))
{
$authors = array_merge($authors, $this->data['creator']);
}
if (!empty($authors))
{
return array_unique($authors);
}
else
{
return false;
}
}
function get_date($date_format = 'j F Y, g:i a')
{
if (!empty($this->data['pubdate']))
{
return date($date_format, $this->data['pubdate']);
}
else if (!empty($this->data['dc:date']))
{
return date($date_format, $this->data['dc:date']);
}
else if (!empty($this->data['issued']))
{
return date($date_format, $this->data['issued']);
}
else if (!empty($this->data['published']))
{
return date($date_format, $this->data['published']);
}
else if (!empty($this->data['modified']))
{
return date($date_format, $this->data['modified']);
}
else if (!empty($this->data['updated']))
{
return date($date_format, $this->data['updated']);
}
else
{
return false;
}
}
function get_permalink()
{
$link = $this->get_link(0);
$enclosure = $this->get_enclosure(0);
if (!empty($link))
{
return $link;
}
else if (!empty($enclosure))
{
return $enclosure->get_link();
}
else
{
return false;
}
}
function get_link($key = 0, $rel = 'alternate')
{
$links = $this->get_links($rel);
if (!empty($links[$key]))
{
return $links[$key];
}
else
{
return false;
}
}
function get_links($rel = 'alternate')
{
if ($rel == 'alternate')
{
$links = array();
if (!empty($this->data['link'][$rel]))
{
$links = $this->data['link'][$rel];
}
if (!empty($this->data['guid']['data']) && $this->data['guid']['permalink'] == true)
{
$links[] = $this->data['guid']['data'];
}
return $links;
}
else if (!empty($this->data['link'][$rel]))
{
return $this->data['link'][$rel];
}
else
{
return false;
}
}
function get_enclosure($key = 0)
{
$enclosures = $this->get_enclosures();
if (!empty($enclosures[$key]))
{
return $enclosures[$key];
}
else
{
return false;
}
}
function get_enclosures()
{
$enclosures = array();
$links = $this->get_links('enclosure');
if (!empty($this->data['enclosures']))
{
$enclosures = array_merge($enclosures, $this->data['enclosures']);
}
if (!empty($links))
{
$enclosures = array_merge($enclosures, $links);
}
if (!empty($enclosures))
{
return array_unique($enclosures);
}
else
{
return false;
}
}
function add_to_blinklist()
{
if ($this->get_permalink())
{
$url = 'http://www.blinklist.com/index.php?Action=Blink/addblink.php&amp;Description=&amp;Url=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;Title=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_blogmarks()
{
if ($this->get_permalink())
{
$url = 'http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;title=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_delicious()
{
if ($this->get_permalink())
{
$url = 'http://del.icio.us/post/?v=3&amp;url=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;title=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_digg()
{
if ($this->get_permalink())
{
return 'http://digg.com/submit?phase=2&amp;URL=' . rawurlencode($this->get_permalink());
}
else
{
return false;
}
}
function add_to_furl()
{
if ($this->get_permalink())
{
$url = 'http://www.furl.net/storeIt.jsp?u=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;t=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_magnolia()
{
if ($this->get_permalink())
{
$url = 'http://ma.gnolia.com/bookmarklet/add?url=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;title=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_myweb20()
{
if ($this->get_permalink())
{
$url = 'http://myweb2.search.yahoo.com/myresults/bookmarklet?u=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;t=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_newsvine()
{
if ($this->get_permalink())
{
$url = 'http://www.newsvine.com/_wine/save?u=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;h=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_reddit()
{
if ($this->get_permalink())
{
$url = 'http://reddit.com/submit?url=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;title=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_segnalo()
{
if ($this->get_permalink())
{
$url = 'http://segnalo.com/post.html.php?url=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;title=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_simpy()
{
if ($this->get_permalink())
{
$url = 'http://www.simpy.com/simpy/LinkAdd.do?href=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;title=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_smarking()
{
if ($this->get_permalink())
{
return 'http://smarking.com/editbookmark/?url=' . rawurlencode($this->get_permalink());
}
else
{
return false;
}
}
function add_to_spurl()
{
if ($this->get_permalink())
{
$url = 'http://www.spurl.net/spurl.php?v=3&amp;url=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;title=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function add_to_wists()
{
if ($this->get_permalink())
{
$url = 'http://wists.com/r.php?c=&amp;r=' . rawurlencode($this->get_permalink());
if ($this->get_title())
{
$url .= '&amp;title=' . rawurlencode($this->get_title());
}
return $url;
}
else
{
return false;
}
}
function search_technorati()
{
if ($this->get_permalink())
{
return 'http://www.technorati.com/search/' . rawurlencode($this->get_permalink());
}
else
{
return false;
}
}
}
/**
* @package sapphire
* @subpackage integration
*/
class SimplePie_Author
{
var $name;
var $link;
var $email;
// Constructor, used to input the data
function SimplePie_Author($name, $link, $email)
{
$this->name = $name;
$this->link = $link;
$this->email = $email;
}
function get_name()
{
if (!empty($this->name))
{
return $this->name;
}
else
{
return false;
}
}
function get_link()
{
if (!empty($this->link))
{
return $this->link;
}
else
{
return false;
}
}
function get_email()
{
if (!empty($this->email))
{
return $this->email;
}
else
{
return false;
}
}
}
/**
* @package sapphire
* @subpackage integration
*/
class SimplePie_Enclosure
{
var $link;
var $type;
var $length;
// Constructor, used to input the data
function SimplePie_Enclosure($link, $type, $length)
{
$this->link = $link;
$this->type = $type;
$this->length = $length;
}
function get_link()
{
if (!empty($this->link))
{
if (class_exists('idna_convert'))
{
$idn = new idna_convert;
$this->link = $idn->encode($this->link);
}
return $this->link;
}
else
{
return false;
}
}
function get_extension()
{
if (!empty($this->link))
{
return pathinfo($this->link, PATHINFO_EXTENSION);
}
else
{
return false;
}
}
function get_type()
{
if (!empty($this->type))
{
return $this->type;
}
else
{
return false;
}
}
function get_length()
{
if (!empty($this->length))
{
return $this->length;
}
else
{
return false;
}
}
function get_size()
{
$length = $this->get_length();
if (!empty($length))
{
return round($length/1048576, 2);
}
else
{
return false;
}
}
function native_embed($options='')
{
return $this->embed($options, true);
}
function embed($options = '', $native = false)
{
// Set up defaults
$audio = '';
$video = '';
$alt = '';
$altclass = '';
$loop = 'false';
$width = 'auto';
$height = 'auto';
$bgcolor = '#ffffff';
// Process options and reassign values as necessary
if (is_array($options))
{
extract($options);
}
else
{
$options = explode(',', $options);
foreach($options as $option)
{
$opt = explode(':', $option, 2);
if (isset($opt[0], $opt[1]))
{
$opt[0] = trim($opt[0]);
$opt[1] = trim($opt[1]);
switch ($opt[0])
{
case 'audio':
$audio = $opt[1];
break;
case 'video':
$video = $opt[1];
break;
case 'alt':
$alt = $opt[1];
break;
case 'altclass':
$altclass = $opt[1];
break;
case 'loop':
$loop = $opt[1];
break;
case 'width':
$width = $opt[1];
break;
case 'height':
$height = $opt[1];
break;
case 'bgcolor':
$bgcolor = $opt[1];
break;
}
}
}
}
$type = strtolower($this->get_type());
// If we encounter an unsupported mime-type, check the file extension and guess intelligently.
if (!in_array($type, array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mpeg', 'audio/x-mpeg', 'audio/mp3', 'x-audio/mp3', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video', 'application/x-shockwave-flash', 'application/futuresplash', 'application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx')))
{
switch (strtolower($this->get_extension()))
{
// Audio mime-types
case 'aac':
case 'adts':
$type = 'audio/acc';
break;
case 'aif':
case 'aifc':
case 'aiff':
case 'cdda':
$type = 'audio/aiff';
break;
case 'bwf':
$type = 'audio/wav';
break;
case 'kar':
case 'mid':
case 'midi':
case 'smf':
$type = 'audio/midi';
break;
case 'm4a':
$type = 'audio/x-m4a';
break;
case 'mp3':
case 'swa':
$type = 'audio/mp3';
break;
case 'wav':
$type = 'audio/wav';
break;
case 'wax':
$type = 'audio/x-ms-wax';
break;
case 'wma':
$type = 'audio/x-ms-wma';
break;
// Video mime-types
case '3gp':
case '3gpp':
$type = 'video/3gpp';
break;
case '3g2':
case '3gp2':
$type = 'video/3gpp2';
break;
case 'asf':
$type = 'video/x-ms-asf';
break;
case 'm1a':
case 'm1s':
case 'm1v':
case 'm15':
case 'm75':
case 'mp2':
case 'mpa':
case 'mpeg':
case 'mpg':
case 'mpm':
case 'mpv':
$type = 'video/mpeg';
break;
case 'm4v':
$type = 'video/x-m4v';
break;
case 'mov':
case 'qt':
$type = 'video/quicktime';
break;
case 'mp4':
case 'mpg4':
$type = 'video/mp4';
break;
case 'sdv':
$type = 'video/sd-video';
break;
case 'wm':
$type = 'video/x-ms-wm';
break;
case 'wmv':
$type = 'video/x-ms-wmv';
break;
case 'wvx':
$type = 'video/x-ms-wvx';
break;
// Flash mime-types
case 'spl':
$type = 'application/futuresplash';
break;
case 'swf':
$type = 'application/x-shockwave-flash';
break;
}
}
$mime = explode('/', $type, 2);
$mime = $mime[0];
// Process values for 'auto'
if ($width == 'auto')
{
if ($mime == 'video')
{
$width = '320';
}
else
{
$width = '100%';
}
}
if ($height == 'auto')
{
if ($mime == 'audio')
{
$height = 0;
}
else if ($mime == 'video')
{
$height = 240;
}
else
{
$height = 256;
}
}
// Set proper placeholder value
if ($mime == 'audio')
{
$placeholder = $audio;
}
else if ($mime == 'video')
{
$placeholder = $video;
}
$embed = '';
// Make sure the JS library is included
// (I know it'll be included multiple times, but I can't think of a better way to do this automatically)
if (!$native)
{
$embed .= '<script type="text/javascript" src="?js"></script>';
}
// Odeo Feed MP3's
if (substr(strtolower($this->get_link()), 0, 15) == 'http://odeo.com') {
if ($native)
{
$embed .= '<embed src="http://odeo.com/flash/audio_player_fullsize.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="440" height="80" wmode="transparent" allowScriptAccess="any" flashvars="valid_sample_rate=true&external_url=' . $this->get_link() . '"></embed>';
}
else
{
$embed .= '<script type="text/javascript">embed_odeo("' . $this->get_link() . '");</script>';
}
}
// QuickTime 7 file types. Need to test with QuickTime 6.
else if (in_array($type, array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mpeg', 'audio/x-mpeg', 'audio/mp3', 'x-audio/mp3', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video')))
{
$height += 16;
if ($native)
{
if ($placeholder != "") {
$embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://www.apple.com/quicktime/download/\"></embed>";
}
else {
$embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width+\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://www.apple.com/quicktime/download/\"></embed>";
}
}
else
{
$embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>";
}
}
// Flash
else if (in_array($type, array('application/x-shockwave-flash', 'application/futuresplash')))
{
if ($native)
{
$embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>";
}
else
{
$embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>";
}
}
// Windows Media
else if (in_array($type, array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx')))
{
$height += 45;
if ($native)
{
$embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>";
}
else
{
$embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>";
}
}
// Everything else
else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>';
return $embed;
}
}
/**
* @package sapphire
* @subpackage integration
*/
class SimplePie_File
{
var $url;
var $useragent;
var $success = true;
var $headers = array();
var $body;
var $fp;
var $redirects = 0;
var $error;
var $method;
function SimplePie_File($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false)
{
if (class_exists('idna_convert'))
{
$idn = new idna_convert;
$url = $idn->encode($url);
}
$this->url = $url;
$this->useragent = $useragent;
if (preg_match('/^http(s)?:\/\//i', $url))
{
if (empty($useragent))
{
$useragent = ini_get('user_agent');
$this->useragent = $useragent;
}
if (!is_array($headers))
{
$headers = array();
}
if (extension_loaded('curl') && version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>=') && !$force_fsockopen)
{
$this->method = 'curl';
$fp = curl_init();
$headers2 = array();
foreach ($headers as $key => $value)
{
$headers2[] = "$key: $value";
}
curl_setopt($fp, CURLOPT_ENCODING, '');
curl_setopt($fp, CURLOPT_URL, $url);
curl_setopt($fp, CURLOPT_HEADER, 1);
curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
curl_setopt($fp, CURLOPT_REFERER, $url);
curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
if (!ini_get('open_basedir') && !ini_get('safe_mode'))
{
curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects);
}
$this->headers = trim(curl_exec($fp));
if (curl_errno($fp) == 23 || curl_errno($fp) == 61)
{
curl_setopt($fp, CURLOPT_ENCODING, 'none');
$this->headers = trim(curl_exec($fp));
}
if (curl_errno($fp))
{
$this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
$this->success = false;
return false;
}
$info = curl_getinfo($fp);
$this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 2);
if (count($this->headers) == $info['redirect_count'] + 1)
{
$this->headers = array_pop($this->headers);
$this->body = '';
}
else
{
$this->body = array_pop($this->headers);
$this->headers = array_pop($this->headers);
}
$this->headers = $this->parse_headers($this->headers);
if (($this->headers['status']['code'] == 301 || $this->headers['status']['code'] == 302 || $this->headers['status']['code'] == 303 || $this->headers['status']['code'] == 307) && !empty($this->headers['location']) && $this->redirects < $redirects)
{
$this->redirects++;
return $this->SimplePie_File($this->headers['location'], $timeout, $redirects, $headers, $useragent, $force_fsockopen);
}
}
else
{
$this->method = 'fsockopen';
$url_parts = parse_url($url);
if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) == 'https')
{
$url_parts['host'] = "ssl://$url_parts[host]";
$url_parts['port'] = 443;
}
if (!isset($url_parts['port']))
{
$url_parts['port'] = 80;
}
$this->fp = fsockopen($url_parts['host'], $url_parts['port'], $errno, $errstr, $timeout);
if (!$this->fp)
{
$this->error = 'fsockopen error: ' . $errstr;
$this->success = false;
return false;
}
else
{
stream_set_timeout($this->fp, $timeout);
$get = (isset($url_parts['query'])) ? "$url_parts[path]?$url_parts[query]" : $url_parts['path'];
$out = "GET $get HTTP/1.0\r\n";
$out .= "Host: $url_parts[host]\r\n";
$out .= "User-Agent: $useragent\r\n";
if (function_exists('gzinflate'))
{
$out .= "Accept-Encoding: gzip,deflate\r\n";
}
if (!empty($url_parts['user']) && !empty($url_parts['pass']))
{
$out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
}
foreach ($headers as $key => $value)
{
$out .= "$key: $value\r\n";
}
$out .= "Connection: Close\r\n\r\n";
fwrite($this->fp, $out);
$info = stream_get_meta_data($this->fp);
$data = '';
while (strpos($data, "\r\n\r\n") === false && !$info['timed_out'])
{
$data .= fgets($this->fp, 128);
$info = stream_get_meta_data($this->fp);
}
if (!$info['timed_out'])
{
$this->headers = $this->parse_headers($data);
if (($this->headers['status']['code'] == 301 || $this->headers['status']['code'] == 302 || $this->headers['status']['code'] == 303 || $this->headers['status']['code'] == 307) && !empty($this->headers['location']) && $this->redirects < $redirects)
{
$this->redirects++;
return $this->SimplePie_File($this->headers['location'], $timeout, $redirects, $headers, $useragent, $force_fsockopen);
}
}
else
{
$this->close();
$this->error = 'fsocket timed out';
$this->success = false;
return false;
}
}
}
return $this->headers['status']['code'];
}
else
{
$this->method = 'fopen';
if ($this->fp = fopen($url, 'r'))
{
return true;
}
else
{
$this->error = 'fopen could not open the file';
$this->success = false;
return false;
}
}
}
function headers()
{
return $this->headers;
}
function body()
{
if (is_null($this->body))
{
if ($this->fp)
{
$info = stream_get_meta_data($this->fp);
$this->body = '';
while (!$info['eof'] && !$info['timed_out'])
{
$this->body .= fread($this->fp, 1024);
$info = stream_get_meta_data($this->fp);
}
if (!$info['timed_out'])
{
$this->body = trim($this->body);
if ($this->method == 'fsockopen' && !empty($this->headers['content-encoding']) && ($this->headers['content-encoding'] == 'gzip' || $this->headers['content-encoding'] == 'deflate'))
{
if (substr($this->body, 0, 8) == "\x1f\x8b\x08\x00\x00\x00\x00\x00")
{
$this->body = substr($this->body, 10);
}
$this->body = gzinflate($this->body);
}
$this->close();
}
else
{
return false;
}
}
else
{
return false;
}
}
return $this->body;
}
function close()
{
if (!is_null($this->fp))
{
if (fclose($this->fp))
{
$this->fp = null;
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
function parse_headers($headers)
{
$headers = explode("\r\n", trim($headers));
$status = array_shift($headers);
foreach ($headers as $header)
{
$data = explode(':', $header, 2);
$head[strtolower(trim($data[0]))] = trim($data[1]);
}
if (preg_match('/HTTP\/[0-9\.]+ ([0-9]+)(.*)$/i', $status, $matches))
{
if (isset($head['status']))
{
unset($head['status']);
}
$head['status']['code'] = $matches[1];
$head['status']['name'] = trim($matches[2]);
}
return $head;
}
}
/**
* @package sapphire
* @subpackage integration
*/
class SimplePie_Cache
{
var $location;
var $filename;
var $extension;
var $name;
function SimplePie_Cache($location, $filename, $extension)
{
$this->location = $location;
$this->filename = rawurlencode($filename);
$this->extension = rawurlencode($extension);
$this->name = "$location/$this->filename.$this->extension";
}
function save($data)
{
if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location))
{
$fp = fopen($this->name, 'w');
if ($fp)
{
fwrite($fp, serialize($data));
fclose($fp);
return true;
}
}
return false;
}
function load()
{
if (file_exists($this->name) && is_readable($this->name))
{
return unserialize(file_get_contents($this->name));
}
return false;
}
function mtime()
{
if (file_exists($this->name))
{
return filemtime($this->name);
}
return false;
}
function touch()
{
if (file_exists($this->name))
{
return touch($this->name);
}
return false;
}
function unlink()
{
if (file_exists($this->name))
{
return unlink($this->name);
}
return false;
}
}
/**
* @package sapphire
* @subpackage integration
*/
class SimplePie_Misc
{
function absolutize_url($relative, $base)
{
$relative = trim($relative);
$base = trim($base);
if (!empty($relative))
{
$relative = SimplePie_Misc::parse_url($relative, false);
$relative = array('scheme' => $relative[2], 'authority' => $relative[3], 'path' => $relative[5], 'query' => $relative[7], 'fragment' => $relative[9]);
if (!empty($relative['scheme']))
{
$target = $relative;
}
else if (!empty($base))
{
$base = SimplePie_Misc::parse_url($base, false);
$base = array('scheme' => $base[2], 'authority' => $base[3], 'path' => $base[5], 'query' => $base[7], 'fragment' => $base[9]);
$target['scheme'] = $base['scheme'];
if (!empty($relative['authority']))
{
$target = array_merge($relative, $target);
}
else
{
$target['authority'] = $base['authority'];
if (!empty($relative['path']))
{
if (strpos($relative['path'], '/') === 0)
{
$target['path'] = $relative['path'];
}
else
{
if (!empty($base['path']))
{
$target['path'] = dirname("$base[path].") . '/' . $relative['path'];
}
else
{
$target['path'] = '/' . $relative['path'];
}
}
if (!empty($relative['query']))
{
$target['query'] = $relative['query'];
}
$input = $target['path'];
$target['path'] = '';
while (!empty($input))
{
if (strpos($input, '../') === 0)
{
$input = substr($input, 3);
}
else if (strpos($input, './') === 0)
{
$input = substr($input, 2);
}
else if (strpos($input, '/./') === 0)
{
$input = substr_replace($input, '/', 0, 3);
}
else if (strpos($input, '/.') === 0 && SimplePie_Misc::strendpos($input, '/.') === 0)
{
$input = substr_replace($input, '/', -2);
}
else if (strpos($input, '/../') === 0)
{
$input = substr_replace($input, '/', 0, 4);
$target['path'] = preg_replace('/(\/)?([^\/]+)$/msiU', '', $target['path']);
}
else if (strpos($input, '/..') === 0 && SimplePie_Misc::strendpos($input, '/..') === 0)
{
$input = substr_replace($input, '/', 0, 3);
$target['path'] = preg_replace('/(\/)?([^\/]+)$/msiU', '', $target['path']);
}
else if ($input == '.' || $input == '..')
{
$input = '';
}
else
{
if (preg_match('/^(.+)(\/|$)/msiU', $input, $match))
{
$target['path'] .= $match[1];
$input = substr_replace($input, '', 0, strlen($match[1]));
}
}
}
}
else
{
if (!empty($base['path']))
{
$target['path'] = $base['path'];
}
else
{
$target['path'] = '/';
}
if (!empty($relative['query']))
{
$target['query'] = $relative['query'];
}
else if (!empty($base['query']))
{
$target['query'] = $base['query'];
}
}
}
if (!empty($relative['fragment']))
{
$target['fragment'] = $relative['fragment'];
}
}
else
{
return false;
}
$return = '';
if (!empty($target['scheme']))
{
$return .= "$target[scheme]:";
}
if (!empty($target['authority']))
{
$return .= $target['authority'];
}
if (!empty($target['path']))
{
$return .= $target['path'];
}
if (!empty($target['query']))
{
$return .= "?$target[query]";
}
if (!empty($target['fragment']))
{
$return .= "#$target[fragment]";
}
}
else
{
$return = $base;
}
return $return;
}
function strendpos($haystack, $needle)
{
return strlen($haystack) - strpos($haystack, $needle) - strlen($needle);
}
function get_element($realname, $string)
{
$return = array();
$name = preg_quote($realname, '/');
preg_match_all("/<($name)((\s*((\w+:)?\w+)\s*=\s*(\"([^\"]*)\"|'([^']*)'|(.*)))*)\s*((\/)?>|>(.*)<\/$name>)/msiU", $string, $matches, PREG_SET_ORDER);
for ($i = 0; $i < count($matches); $i++)
{
$return[$i]['tag'] = $realname;
$return[$i]['full'] = $matches[$i][0];
if (strlen($matches[$i][10]) <= 2)
{
$return[$i]['self_closing'] = true;
}
else
{
$return[$i]['self_closing'] = false;
$return[$i]['content'] = $matches[$i][12];
}
$return[$i]['attribs'] = array();
if (!empty($matches[$i][2]))
{
preg_match_all('/((\w+:)?\w+)\s*=\s*("([^"]*)"|\'([^\']*)\'|(\S+))\s/msiU', ' ' . $matches[$i][2] . ' ', $attribs, PREG_SET_ORDER);
for ($j = 0; $j < count($attribs); $j++)
{
$return[$i]['attribs'][strtoupper($attribs[$j][1])]['data'] = $attribs[$j][count($attribs[$j])-1];
$first = substr($attribs[$j][2], 0, 1);
$return[$i]['attribs'][strtoupper($attribs[$j][1])]['split'] = ($first == '"' || $first == "'") ? $first : '"';
}
}
}
return $return;
}
function element_implode($element)
{
$full = "<$element[tag]";
foreach ($element['attribs'] as $key => $value)
{
$key = strtolower($key);
$full .= " $key=$value[split]$value[data]$value[split]";
}
if ($element['self_closing'])
{
$full .= ' />';
}
else
{
$full .= ">$element[content]</$element[tag]>";
}
return $full;
}
function error($message, $level, $file, $line)
{
switch ($level)
{
case E_USER_ERROR:
$note = 'PHP Error';
break;
case E_USER_WARNING:
$note = 'PHP Warning';
break;
case E_USER_NOTICE:
$note = 'PHP Notice';
break;
default:
$note = 'Unknown Error';
break;
}
error_log("$note: $message in $file on line $line", 0);
return $message;
}
function display_file($url, $timeout = 10, $useragent = null)
{
$file = new SimplePie_File($url, $timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $useragent);
$headers = $file->headers();
if ($file->body() !== false)
{
header('Content-type: ' . $headers['content-type']);
echo $file->body();
exit;
}
}
function fix_protocol($url, $http = 1)
{
$parsed = SimplePie_Misc::parse_url($url);
if (!empty($parsed['scheme']) && strtolower($parsed['scheme']) != 'http' && strtolower($parsed['scheme']) != 'https')
{
return SimplePie_Misc::fix_protocol("$parsed[authority]$parsed[path]$parsed[query]$parsed[fragment]", $http);
}
if (!file_exists($url) && empty($parsed['scheme']))
{
return SimplePie_Misc::fix_protocol("http://$url", $http);
}
if ($http == 2 && !empty($parsed['scheme']))
{
return "feed:$url";
}
else if ($http == 3 && strtolower($parsed['scheme']) == 'http')
{
return substr_replace($url, 'podcast', 0, 4);
}
else
{
return $url;
}
}
function parse_url($url, $parse_match = true)
{
preg_match('/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/i', $url, $match);
if (empty($match[0]))
{
return false;
}
else
{
for ($i = 6; $i < 10; $i++)
{
if (!isset($match[$i]))
{
$match[$i] = '';
}
}
if ($parse_match)
{
$match = array('scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5], 'query' => $match[6], 'fragment' => $match[8]);
}
return $match;
}
}
function change_encoding($data, $input, $output)
{
$input = SimplePie_Misc::encoding($input);
$output = SimplePie_Misc::encoding($output);
if ($input != $output)
{
if (function_exists('iconv') && $input['use_iconv'] && $output['use_iconv'] && iconv($input['encoding'], "$output[encoding]//TRANSLIT", $data))
{
return iconv($input['encoding'], "$output[encoding]//TRANSLIT", $data);
}
else if (function_exists('iconv') && $input['use_iconv'] && $output['use_iconv'] && iconv($input['encoding'], $output['encoding'], $data))
{
return iconv($input['encoding'], $output['encoding'], $data);
}
else if (function_exists('mb_convert_encoding') && $input['use_mbstring'] && $output['use_mbstring'])
{
return mb_convert_encoding($data, $output['encoding'], $input['encoding']);
}
else if ($input['encoding'] == 'ISO-8859-1' && $output['encoding'] == 'UTF-8')
{
return utf8_encode($data);
}
else if ($input['encoding'] == 'UTF-8' && $output['encoding'] == 'ISO-8859-1')
{
return utf8_decode($data);
}
}
return $data;
}
function encoding($encoding)
{
$return['use_mbstring'] = false;
$return['use_iconv'] = false;
switch (strtolower($encoding))
{
// 7bit
case '7bit':
case '7-bit':
$return['encoding'] = '7bit';
$return['use_mbstring'] = true;
break;
// 8bit
case '8bit':
case '8-bit':
$return['encoding'] = '8bit';
$return['use_mbstring'] = true;
break;
// ARMSCII-8
case 'armscii-8':
case 'armscii':
$return['encoding'] = 'ARMSCII-8';
$return['use_iconv'] = true;
break;
// ASCII
case 'us-ascii':
case 'ascii':
$return['encoding'] = 'US-ASCII';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// BASE64
case 'base64':
case 'base-64':
$return['encoding'] = 'BASE64';
$return['use_mbstring'] = true;
break;
// Big5 - Traditional Chinese, mainly used in Taiwan
case 'big5':
case '950':
$return['encoding'] = 'BIG5';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// Big5 with Hong Kong extensions, Traditional Chinese
case 'big5-hkscs':
$return['encoding'] = 'BIG5-HKSCS';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// byte2be
case 'byte2be':
$return['encoding'] = 'byte2be';
$return['use_mbstring'] = true;
break;
// byte2le
case 'byte2le':
$return['encoding'] = 'byte2le';
$return['use_mbstring'] = true;
break;
// byte4be
case 'byte4be':
$return['encoding'] = 'byte4be';
$return['use_mbstring'] = true;
break;
// byte4le
case 'byte4le':
$return['encoding'] = 'byte4le';
$return['use_mbstring'] = true;
break;
// EUC-CN
case 'euc-cn':
case 'euccn':
$return['encoding'] = 'EUC-CN';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// EUC-JISX0213
case 'euc-jisx0213':
case 'eucjisx0213':
$return['encoding'] = 'EUC-JISX0213';
$return['use_iconv'] = true;
break;
// EUC-JP
case 'euc-jp':
case 'eucjp':
$return['encoding'] = 'EUC-JP';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// EUCJP-win
case 'euc-jp-win':
case 'eucjp-win':
case 'eucjpwin':
$return['encoding'] = 'EUCJP-win';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// EUC-KR
case 'euc-kr':
case 'euckr':
$return['encoding'] = 'EUC-KR';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// EUC-TW
case 'euc-tw':
case 'euctw':
$return['encoding'] = 'EUC-TW';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// GB18030 - Simplified Chinese, national standard character set
case 'gb18030-2000':
case 'gb18030':
$return['encoding'] = 'GB18030';
$return['use_iconv'] = true;
break;
// GB2312 - Simplified Chinese, national standard character set
case 'gb2312':
case '936':
$return['encoding'] = 'GB2312';
$return['use_mbstring'] = true;
break;
// GBK
case 'gbk':
$return['encoding'] = 'GBK';
$return['use_iconv'] = true;
break;
// Georgian-Academy
case 'georgian-academy':
$return['encoding'] = 'Georgian-Academy';
$return['use_iconv'] = true;
break;
// Georgian-PS
case 'georgian-ps':
$return['encoding'] = 'Georgian-PS';
$return['use_iconv'] = true;
break;
// HTML-ENTITIES
case 'html-entities':
case 'htmlentities':
$return['encoding'] = 'HTML-ENTITIES';
$return['use_mbstring'] = true;
break;
// HZ
case 'hz':
$return['encoding'] = 'HZ';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-2022-CN
case 'iso-2022-cn':
case 'iso2022-cn':
case 'iso2022cn':
$return['encoding'] = 'ISO-2022-CN';
$return['use_iconv'] = true;
break;
// ISO-2022-CN-EXT
case 'iso-2022-cn-ext':
case 'iso2022-cn-ext':
case 'iso2022cn-ext':
case 'iso2022cnext':
$return['encoding'] = 'ISO-2022-CN';
$return['use_iconv'] = true;
break;
// ISO-2022-JP
case 'iso-2022-jp':
case 'iso2022-jp':
case 'iso2022jp':
$return['encoding'] = 'ISO-2022-JP';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-2022-JP-1
case 'iso-2022-jp-1':
case 'iso2022-jp-1':
case 'iso2022jp-1':
case 'iso2022jp1':
$return['encoding'] = 'ISO-2022-JP-1';
$return['use_iconv'] = true;
break;
// ISO-2022-JP-2
case 'iso-2022-jp-2':
case 'iso2022-jp-2':
case 'iso2022jp-2':
case 'iso2022jp2':
$return['encoding'] = 'ISO-2022-JP-2';
$return['use_iconv'] = true;
break;
// ISO-2022-JP-3
case 'iso-2022-jp-3':
case 'iso2022-jp-3':
case 'iso2022jp-3':
case 'iso2022jp3':
$return['encoding'] = 'ISO-2022-JP-3';
$return['use_iconv'] = true;
break;
// ISO-2022-KR
case 'iso-2022-kr':
case 'iso2022-kr':
case 'iso2022kr':
$return['encoding'] = 'ISO-2022-KR';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-1
case 'iso-8859-1':
case 'iso8859-1':
$return['encoding'] = 'ISO-8859-1';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-2
case 'iso-8859-2':
case 'iso8859-2':
$return['encoding'] = 'ISO-8859-2';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-3
case 'iso-8859-3':
case 'iso8859-3':
$return['encoding'] = 'ISO-8859-3';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-4
case 'iso-8859-4':
case 'iso8859-4':
$return['encoding'] = 'ISO-8859-4';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-5
case 'iso-8859-5':
case 'iso8859-5':
$return['encoding'] = 'ISO-8859-5';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-6
case 'iso-8859-6':
case 'iso8859-6':
$return['encoding'] = 'ISO-8859-6';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-7
case 'iso-8859-7':
case 'iso8859-7':
$return['encoding'] = 'ISO-8859-7';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-8
case 'iso-8859-8':
case 'iso8859-8':
$return['encoding'] = 'ISO-8859-8';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-9
case 'iso-8859-9':
case 'iso8859-9':
$return['encoding'] = 'ISO-8859-9';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-10
case 'iso-8859-10':
case 'iso8859-10':
$return['encoding'] = 'ISO-8859-10';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// mbstring/iconv functions don't appear to support 11 & 12
// ISO-8859-13
case 'iso-8859-13':
case 'iso8859-13':
$return['encoding'] = 'ISO-8859-13';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-14
case 'iso-8859-14':
case 'iso8859-14':
$return['encoding'] = 'ISO-8859-14';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-15
case 'iso-8859-15':
case 'iso8859-15':
$return['encoding'] = 'ISO-8859-15';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// ISO-8859-16
case 'iso-8859-16':
case 'iso8859-16':
$return['encoding'] = 'ISO-8859-16';
$return['use_iconv'] = true;
break;
// JIS
case 'jis':
$return['encoding'] = 'JIS';
$return['use_mbstring'] = true;
break;
// JOHAB - Korean
case 'johab':
$return['encoding'] = 'JOHAB';
$return['use_iconv'] = true;
break;
// Russian
case 'koi8-r':
case 'koi8r':
$return['encoding'] = 'KOI8-R';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// Turkish
case 'koi8-t':
case 'koi8t':
$return['encoding'] = 'KOI8-T';
$return['use_iconv'] = true;
break;
// Ukrainian
case 'koi8-u':
case 'koi8u':
$return['encoding'] = 'KOI8-U';
$return['use_iconv'] = true;
break;
// Russian+Ukrainian
case 'koi8-ru':
case 'koi8ru':
$return['encoding'] = 'KOI8-RU';
$return['use_iconv'] = true;
break;
// Macintosh (Mac OS Classic)
case 'macintosh':
$return['encoding'] = 'Macintosh';
$return['use_iconv'] = true;
break;
// MacArabic (Mac OS Classic)
case 'macarabic':
$return['encoding'] = 'MacArabic';
$return['use_iconv'] = true;
break;
// MacCentralEurope (Mac OS Classic)
case 'maccentraleurope':
$return['encoding'] = 'MacCentralEurope';
$return['use_iconv'] = true;
break;
// MacCroatian (Mac OS Classic)
case 'maccroatian':
$return['encoding'] = 'MacCroatian';
$return['use_iconv'] = true;
break;
// MacCyrillic (Mac OS Classic)
case 'maccyrillic':
$return['encoding'] = 'MacCyrillic';
$return['use_iconv'] = true;
break;
// MacGreek (Mac OS Classic)
case 'macgreek':
$return['encoding'] = 'MacGreek';
$return['use_iconv'] = true;
break;
// MacHebrew (Mac OS Classic)
case 'machebrew':
$return['encoding'] = 'MacHebrew';
$return['use_iconv'] = true;
break;
// MacIceland (Mac OS Classic)
case 'maciceland':
$return['encoding'] = 'MacIceland';
$return['use_iconv'] = true;
break;
// MacRoman (Mac OS Classic)
case 'macroman':
$return['encoding'] = 'MacRoman';
$return['use_iconv'] = true;
break;
// MacRomania (Mac OS Classic)
case 'macromania':
$return['encoding'] = 'MacRomania';
$return['use_iconv'] = true;
break;
// MacThai (Mac OS Classic)
case 'macthai':
$return['encoding'] = 'MacThai';
$return['use_iconv'] = true;
break;
// MacTurkish (Mac OS Classic)
case 'macturkish':
$return['encoding'] = 'MacTurkish';
$return['use_iconv'] = true;
break;
// MacUkraine (Mac OS Classic)
case 'macukraine':
$return['encoding'] = 'MacUkraine';
$return['use_iconv'] = true;
break;
// MuleLao-1
case 'mulelao-1':
case 'mulelao1':
$return['encoding'] = 'MuleLao-1';
$return['use_iconv'] = true;
break;
// Shift_JIS
case 'shift_jis':
case 'sjis':
case '932':
$return['encoding'] = 'Shift_JIS';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// Shift_JISX0213
case 'shift-jisx0213':
case 'shiftjisx0213':
$return['encoding'] = 'Shift_JISX0213';
$return['use_iconv'] = true;
break;
// SJIS-win
case 'sjis-win':
case 'sjiswin':
case 'shift_jis-win':
$return['encoding'] = 'SJIS-win';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// TCVN - Vietnamese
case 'tcvn':
$return['encoding'] = 'TCVN';
$return['use_iconv'] = true;
break;
// TDS565 - Turkish
case 'tds565':
$return['encoding'] = 'TDS565';
$return['use_iconv'] = true;
break;
// TIS-620 Thai
case 'tis-620':
case 'tis620':
$return['encoding'] = 'TIS-620';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-2
case 'ucs-2':
case 'ucs2':
case 'utf-16':
case 'utf16':
$return['encoding'] = 'UCS-2';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-2BE
case 'ucs-2be':
case 'ucs2be':
case 'utf-16be':
case 'utf16be':
$return['encoding'] = 'UCS-2BE';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-2LE
case 'ucs-2le':
case 'ucs2le':
case 'utf-16le':
case 'utf16le':
$return['encoding'] = 'UCS-2LE';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-2-INTERNAL
case 'ucs-2-internal':
case 'ucs2internal':
$return['encoding'] = 'UCS-2-INTERNAL';
$return['use_iconv'] = true;
break;
// UCS-4
case 'ucs-4':
case 'ucs4':
case 'utf-32':
case 'utf32':
$return['encoding'] = 'UCS-4';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-4BE
case 'ucs-4be':
case 'ucs4be':
case 'utf-32be':
case 'utf32be':
$return['encoding'] = 'UCS-4BE';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-4LE
case 'ucs-4le':
case 'ucs4le':
case 'utf-32le':
case 'utf32le':
$return['encoding'] = 'UCS-4LE';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-4-INTERNAL
case 'ucs-4-internal':
case 'ucs4internal':
$return['encoding'] = 'UCS-4-INTERNAL';
$return['use_iconv'] = true;
break;
// UCS-16
case 'ucs-16':
case 'ucs16':
$return['encoding'] = 'UCS-16';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-16BE
case 'ucs-16be':
case 'ucs16be':
$return['encoding'] = 'UCS-16BE';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-16LE
case 'ucs-16le':
case 'ucs16le':
$return['encoding'] = 'UCS-16LE';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-32
case 'ucs-32':
case 'ucs32':
$return['encoding'] = 'UCS-32';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-32BE
case 'ucs-32be':
case 'ucs32be':
$return['encoding'] = 'UCS-32BE';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UCS-32LE
case 'ucs-32le':
case 'ucs32le':
$return['encoding'] = 'UCS-32LE';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UTF-7
case 'utf-7':
case 'utf7':
$return['encoding'] = 'UTF-7';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// UTF7-IMAP
case 'utf-7-imap':
case 'utf7-imap':
case 'utf7imap':
$return['encoding'] = 'UTF7-IMAP';
$return['use_mbstring'] = true;
break;
// VISCII - Vietnamese ASCII
case 'viscii':
$return['encoding'] = 'VISCII';
$return['use_iconv'] = true;
break;
// Windows-specific Central & Eastern Europe
case 'cp1250':
case 'windows-1250':
case 'win-1250':
case '1250':
$return['encoding'] = 'Windows-1250';
$return['use_iconv'] = true;
break;
// Windows-specific Cyrillic
case 'cp1251':
case 'windows-1251':
case 'win-1251':
case '1251':
$return['encoding'] = 'Windows-1251';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// Windows-specific Western Europe
case 'cp1252':
case 'windows-1252':
case '1252':
$return['encoding'] = 'Windows-1252';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
// Windows-specific Greek
case 'cp1253':
case 'windows-1253':
case '1253':
$return['encoding'] = 'Windows-1253';
$return['use_iconv'] = true;
break;
// Windows-specific Turkish
case 'cp1254':
case 'windows-1254':
case '1254':
$return['encoding'] = 'Windows-1254';
$return['use_iconv'] = true;
break;
// Windows-specific Hebrew
case 'cp1255':
case 'windows-1255':
case '1255':
$return['encoding'] = 'Windows-1255';
$return['use_iconv'] = true;
break;
// Windows-specific Arabic
case 'cp1256':
case 'windows-1256':
case '1256':
$return['encoding'] = 'Windows-1256';
$return['use_iconv'] = true;
break;
// Windows-specific Baltic
case 'cp1257':
case 'windows-1257':
case '1257':
$return['encoding'] = 'Windows-1257';
$return['use_iconv'] = true;
break;
// Windows-specific Vietnamese
case 'cp1258':
case 'windows-1258':
case '1258':
$return['encoding'] = 'Windows-1258';
$return['use_iconv'] = true;
break;
// Default to UTF-8
default:
$return['encoding'] = 'UTF-8';
$return['use_iconv'] = true;
$return['use_mbstring'] = true;
break;
}
// Then, return it.
return $return;
}
function get_curl_version()
{
$curl = 0;
if (is_array(curl_version()))
{
$curl = curl_version();
$curl = $curl['version'];
}
else
{
$curl = curl_version();
$curl = explode(' ', $curl);
$curl = explode('/', $curl[0]);
$curl = $curl[1];
}
return $curl;
}
function is_a_class($class1, $class2)
{
if (class_exists($class1))
{
$classes = array(strtolower($class1));
while ($class1 = get_parent_class($class1))
{
$classes[] = strtolower($class1);
}
return in_array(strtolower($class2), $classes);
}
else
{
return false;
}
}
}
/**
* @package sapphire
* @subpackage integration
*/
class SimplePie_Locator
{
var $useragent;
var $timeout;
var $file;
var $local;
var $elsewhere;
var $file_class = 'SimplePie_File';
function SimplePie_Locator(&$file, $timeout = 10, $useragent = null, $file_class = 'SimplePie_File')
{
if (!is_a($file, 'SimplePie_File'))
{
$this->file = new $this->file_class($file, $timeout, $useragent);
}
else
{
$this->file =& $file;
}
$this->file_class = $file_class;
$this->useragent = $useragent;
$this->timeout = $timeout;
}
function find()
{
if ($this->is_feed($this->file))
{
return $this->file->url;
}
$autodiscovery = $this->autodiscovery($this->file);
if ($autodiscovery)
{
return $autodiscovery;
}
if ($this->get_links($this->file))
{
if (!empty($this->local))
{
$extension_local = $this->extension($this->local);
if ($extension_local)
{
return $extension_local;
}
$body_local = $this->body($this->local);
if ($body_local)
{
return $body_local;
}
}
if (!empty($this->elsewhere))
{
$extension_elsewhere = $this->extension($this->elsewhere);
if ($extension_elsewhere)
{
return $extension_elsewhere;
}
$body_elsewhere = $this->body($this->elsewhere);
if ($body_elsewhere)
{
return $body_elsewhere;
}
}
}
return false;
}
function is_feed(&$file)
{
if (!is_a($file, 'SimplePie_File'))
{
if (isset($this))
{
$file2 = new $this->file_class($file, $this->timeout, 5, null, $this->useragent);
}
else
{
$file2 = new $this->file_class($file);
}
$file2->body();
$file2->close();
}
else
{
$file2 =& $file;
}
$body = preg_replace('/<\!-(.*)-\>/msiU', '', $file2->body());
if (preg_match('/<(\w+\:)?rss/msiU', $body) || preg_match('/<(\w+\:)?RDF/mi', $body) || preg_match('/<(\w+\:)?feed/mi', $body))
{
return true;
}
return false;
}
function autodiscovery(&$file)
{
$links = SimplePie_Misc::get_element('link', $file->body());
$done = array();
foreach ($links as $link)
{
if (!empty($link['attribs']['TYPE']['data']) && !empty($link['attribs']['HREF']['data']) && !empty($link['attribs']['REL']['data']))
{
$rel = preg_split('/\s+/', strtolower(trim($link['attribs']['REL']['data'])));
$type = preg_match('/^(application\/rss\+xml|application\/atom\+xml|application\/rdf\+xml|application\/xml\+rss|application\/xml\+atom|application\/xml\+rdf|application\/xml|application\/x\.atom\+xml|text\/xml)(;|$)/msiU', trim($link['attribs']['TYPE']['data']));
$href = SimplePie_Misc::absolutize_url(trim($link['attribs']['HREF']['data']), $this->file->url);
if (!in_array($href, $done) && in_array('alternate', $rel) && $type)
{
$feed = $this->is_feed($href);
if ($feed)
{
return $href;
}
}
$done[] = $href;
}
}
return false;
}
function get_links(&$file)
{
$links = SimplePie_Misc::get_element('a', $file->body());
foreach ($links as $link)
{
if (!empty($link['attribs']['HREF']['data']))
{
$href = trim($link['attribs']['HREF']['data']);
$parsed = SimplePie_Misc::parse_url($href);
if (empty($parsed['scheme']) || $parsed['scheme'] != 'javascript')
{
$current = SimplePie_Misc::parse_url($this->file->url);
if (empty($parsed['authority']) || $parsed['authority'] == $current['authority'])
{
$this->local[] = SimplePie_Misc::absolutize_url($href, $this->file->url);
}
else
{
$this->elsewhere[] = SimplePie_Misc::absolutize_url($href, $this->file->url);
}
}
}
}
if (!empty($this->local))
{
$this->local = array_unique($this->local);
}
if (!empty($this->elsewhere))
{
$this->elsewhere = array_unique($this->elsewhere);
}
if (!empty($this->local) || !empty($this->elsewhere))
{
return true;
}
return false;
}
function extension(&$array)
{
foreach ($array as $key => $value)
{
$value = SimplePie_Misc::absolutize_url($value, $this->file->url);
if (in_array(strrchr($value, '.'), array('.rss', '.rdf', '.atom', '.xml')))
{
if ($this->is_feed($value))
{
return $value;
}
else
{
unset($array[$key]);
}
}
}
return false;
}
function body(&$array)
{
foreach ($array as $key => $value)
{
$value = SimplePie_Misc::absolutize_url($value, $this->file->url);
if (preg_match('/(rss|rdf|atom|xml)/i', $value))
{
if ($this->is_feed($value))
{
return $value;
}
else
{
unset($array[$key]);
}
}
}
return false;
}
}
/**
* @package sapphire
* @subpackage integration
*/
class SimplePie_Parser
{
var $encoding;
var $data;
var $namespaces = array('xml' => 'HTTP://WWW.W3.ORG/XML/1998/NAMESPACE', 'atom' => 'ATOM', 'rss2' => 'RSS', 'rdf' => 'RDF', 'rss1' => 'RSS', 'dc' => 'DC', 'xhtml' => 'XHTML', 'content' => 'CONTENT');
var $xml;
var $error_code;
var $error_string;
var $current_line;
var $current_column;
var $current_byte;
var $tag_name;
var $inside_item;
var $item_number = 0;
var $inside_channel;
var $author_number= 0;
var $category_number = 0;
var $enclosure_number = 0;
var $link_number = 0;
var $item_link_number = 0;
var $inside_image;
var $attribs;
var $is_first;
var $inside_author;
function SimplePie_Parser($data, $encoding, $return_xml = false)
{
$this->encoding = $encoding;
// Strip BOM:
// UTF-32 Big Endian BOM
if (strpos($data, sprintf('%c%c%c%c', 0x00, 0x00, 0xFE, 0xFF)) === 0)
{
$data = substr($data, 4);
}
// UTF-32 Little Endian BOM
else if (strpos($data, sprintf('%c%c%c%c', 0xFF, 0xFE, 0x00, 0x00)) === 0)
{
$data = substr($data, 4);
}
// UTF-16 Big Endian BOM
else if (strpos($data, sprintf('%c%c', 0xFE, 0xFF)) === 0)
{
$data = substr($data, 2);
}
// UTF-16 Little Endian BOM
else if (strpos($data, sprintf('%c%c', 0xFF, 0xFE)) === 0)
{
$data = substr($data, 2);
}
// UTF-8 BOM
else if (strpos($data, sprintf('%c%c%c', 0xEF, 0xBB, 0xBF)) === 0)
{
$data = substr($data, 3);
}
// Make sure the XML prolog is sane and has the correct encoding
if (preg_match('/^<\?xml(.*)?>/msiU', $data, $prolog))
{
$data = substr_replace($data, '', 0, strlen($prolog[0]));
}
$data = "<?xml version='1.0' encoding='$encoding'?>\n" . $data;
// Put some data into CDATA blocks
// If we're RSS
if ((stristr($data, '<rss') || preg_match('/<([a-z0-9]+\:)?RDF/mi', $data)) && (preg_match('/<([a-z0-9]+\:)?channel/mi', $data) || preg_match('/<([a-z0-9]+\:)?item/mi', $data)))
{
$sp_elements = array(
'author',
'category',
'copyright',
'description',
'docs',
'generator',
'guid',
'language',
'lastBuildDate',
'link',
'managingEditor',
'pubDate',
'title',
'url',
'webMaster',
);
}
// Or if we're Atom
else
{
$sp_elements = array(
'content',
'copyright',
'name',
'subtitle',
'summary',
'tagline',
'title',
);
}
foreach ($sp_elements as $full)
{
$data = preg_replace_callback("/<($full)((\s*((\w+:)?\w+)\s*=\s*(\"([^\"]*)\"|'([^']*)'))*)\s*(\/>|>(.*)<\/$full>)/msiU", array(&$this, 'add_cdata'), $data);
}
foreach ($sp_elements as $full)
{
// Deal with CDATA within CDATA (this can be caused by us inserting CDATA above)
$data = preg_replace_callback("/<($full)((\s*((\w+:)?\w+)\s*=\s*(\"([^\"]*)\"|'([^']*)'))*)\s*(\/>|><!\[CDATA\[(.*)\]\]><\/$full>)/msiU", array(&$this, 'cdata_in_cdata'), $data);
}
// Return the XML, if so desired
if ($return_xml)
{
$this->data =& $data;
return;
}
// Create the parser
$this->xml = xml_parser_create_ns($encoding);
xml_parser_set_option($this->xml, XML_OPTION_SKIP_WHITE, 1);
xml_set_object($this->xml, $this);
xml_set_character_data_handler($this->xml, 'data_handler');
xml_set_element_handler($this->xml, 'start_handler', 'end_handler');
xml_set_start_namespace_decl_handler($this->xml, 'start_name_space');
xml_set_end_namespace_decl_handler($this->xml, 'end_name_space');
// Parse!
if (!xml_parse($this->xml, $data))
{
$this->data = null;
$this->error_code = xml_get_error_code($this->xml);
$this->error_string = xml_error_string($this->error_code);
}
$this->current_line = xml_get_current_line_number($this->xml);
$this->current_column = xml_get_current_column_number($this->xml);
$this->current_byte = xml_get_current_byte_index($this->xml);
xml_parser_free($this->xml);
return;
}
function add_cdata($match)
{
if (isset($match[10]))
{
return "<$match[1]$match[2]><![CDATA[$match[10]]]></$match[1]>";
}
return $match[0];
}
function cdata_in_cdata($match)
{
if (isset($match[10]))
{
$match[10] = preg_replace_callback('/<!\[CDATA\[(.*)\]\]>/msiU', array(&$this, 'real_cdata_in_cdata'), $match[10]);
return "<$match[1]$match[2]><![CDATA[$match[10]]]></$match[1]>";
}
return $match[0];
}
function real_cdata_in_cdata($match)
{
return htmlspecialchars($match[1], ENT_NOQUOTES);
}
function do_add_content(&$array, $data)
{
if ($this->is_first)
{
$array['data'] = $data;
$array['attribs'] = $this->attribs;
}
else
{
$array['data'] .= $data;
}
}
function start_handler($parser, $name, $attribs)
{
$this->tag_name = $name;
$this->attribs = $attribs;
$this->is_first = true;
switch ($this->tag_name)
{
case 'ITEM':
case $this->namespaces['rss2'] . ':ITEM':
case $this->namespaces['rss1'] . ':ITEM':
case 'ENTRY':
case $this->namespaces['atom'] . ':ENTRY':
$this->inside_item = true;
$this->do_add_content($this->data['items'][$this->item_number], '');
break;
case 'CHANNEL':
case $this->namespaces['rss2'] . ':CHANNEL':
case $this->namespaces['rss1'] . ':CHANNEL':
$this->inside_channel = true;
break;
case 'RSS':
case $this->namespaces['rss2'] . ':RSS':
$this->data['feedinfo']['type'] = 'RSS';
$this->do_add_content($this->data['feeddata'], '');
if (!empty($attribs['VERSION']))
{
$this->data['feedinfo']['version'] = trim($attribs['VERSION']);
}
break;
case $this->namespaces['rdf'] . ':RDF':
$this->data['feedinfo']['type'] = 'RSS';
$this->do_add_content($this->data['feeddata'], '');
$this->data['feedinfo']['version'] = 1;
break;
case 'FEED':
case $this->namespaces['atom'] . ':FEED':
$this->data['feedinfo']['type'] = 'Atom';
$this->do_add_content($this->data['feeddata'], '');
if (!empty($attribs['VERSION']))
{
$this->data['feedinfo']['version'] = trim($attribs['VERSION']);
}
break;
case 'IMAGE':
case $this->namespaces['rss2'] . ':IMAGE':
case $this->namespaces['rss1'] . ':IMAGE':
if ($this->inside_channel)
{
$this->inside_image = true;
}
break;
}
if (!empty($this->data['feedinfo']['type']) && $this->data['feedinfo']['type'] == 'Atom' && ($this->tag_name == 'AUTHOR' || $this->tag_name == $this->namespaces['atom'] . ':AUTHOR'))
{
$this->inside_author = true;
}
$this->data_handler($this->xml, '');
}
function data_handler($parser, $data)
{
if ($this->inside_item)
{
switch ($this->tag_name)
{
case 'TITLE':
case $this->namespaces['rss1'] . ':TITLE':
case $this->namespaces['rss2'] . ':TITLE':
case $this->namespaces['atom'] . ':TITLE':
$this->do_add_content($this->data['items'][$this->item_number]['title'], $data);
break;
case $this->namespaces['dc'] . ':TITLE':
$this->do_add_content($this->data['items'][$this->item_number]['dc:title'], $data);
break;
case 'CONTENT':
case $this->namespaces['atom'] . ':CONTENT':
$this->do_add_content($this->data['items'][$this->item_number]['content'], $data);
break;
case $this->namespaces['content'] . ':ENCODED':
$this->do_add_content($this->data['items'][$this->item_number]['encoded'], $data);
break;
case 'SUMMARY':
case $this->namespaces['atom'] . ':SUMMARY':
$this->do_add_content($this->data['items'][$this->item_number]['summary'], $data);
break;
case 'LONGDESC':
$this->do_add_content($this->data['items'][$this->item_number]['longdesc'], $data);
break;
case 'DESCRIPTION':
case $this->namespaces['rss1'] . ':DESCRIPTION':
case $this->namespaces['rss2'] . ':DESCRIPTION':
$this->do_add_content($this->data['items'][$this->item_number]['description'], $data);
break;
case $this->namespaces['dc'] . ':DESCRIPTION':
$this->do_add_content($this->data['items'][$this->item_number]['dc:description'], $data);
break;
case 'LINK':
case $this->namespaces['rss1'] . ':LINK':
case $this->namespaces['rss2'] . ':LINK':
case $this->namespaces['atom'] . ':LINK':
$this->do_add_content($this->data['items'][$this->item_number]['link'][$this->item_link_number], $data);
break;
case 'ENCLOSURE':
case $this->namespaces['rss1'] . ':ENCLOSURE':
case $this->namespaces['rss2'] . ':ENCLOSURE':
case $this->namespaces['atom'] . ':ENCLOSURE':
$this->do_add_content($this->data['items'][$this->item_number]['enclosure'][$this->enclosure_number], $data);
break;
case 'GUID':
case $this->namespaces['rss1'] . ':GUID':
case $this->namespaces['rss2'] . ':GUID':
$this->do_add_content($this->data['items'][$this->item_number]['guid'], $data);
break;
case 'ID':
case $this->namespaces['atom'] . ':ID':
$this->do_add_content($this->data['items'][$this->item_number]['id'], $data);
break;
case 'PUBDATE':
case $this->namespaces['rss1'] . ':PUBDATE':
case $this->namespaces['rss2'] . ':PUBDATE':
$this->do_add_content($this->data['items'][$this->item_number]['pubdate'], $data);
break;
case $this->namespaces['dc'] . ':DATE':
$this->do_add_content($this->data['items'][$this->item_number]['dc:date'], $data);
break;
case 'ISSUED':
case $this->namespaces['atom'] . ':ISSUED':
$this->do_add_content($this->data['items'][$this->item_number]['issued'], $data);
break;
case 'PUBLISHED':
case $this->namespaces['atom'] . ':PUBLISHED':
$this->do_add_content($this->data['items'][$this->item_number]['published'], $data);
break;
case 'MODIFIED':
case $this->namespaces['atom'] . ':MODIFIED':
$this->do_add_content($this->data['items'][$this->item_number]['modified'], $data);
break;
case 'UPDATED':
case $this->namespaces['atom'] . ':UPDATED':
$this->do_add_content($this->data['items'][$this->item_number]['updated'], $data);
break;
case 'CATEGORY':
case $this->namespaces['rss1'] . ':CATEGORY':
case $this->namespaces['rss2'] . ':CATEGORY':
case $this->namespaces['atom'] . ':CATEGORY':
$this->do_add_content($this->data['items'][$this->item_number]['category'][$this->category_number], $data);
break;
case $this->namespaces['dc'] . ':SUBJECT':
$this->do_add_content($this->data['items'][$this->item_number]['subject'][$this->category_number], $data);
break;
case $this->namespaces['dc'] . ':CREATOR':
$this->do_add_content($this->data['items'][$this->item_number]['creator'][$this->author_number], $data);
break;
case 'AUTHOR':
case $this->namespaces['rss1'] . ':AUTHOR':
case $this->namespaces['rss2'] . ':AUTHOR':
$this->do_add_content($this->data['items'][$this->item_number]['author'][$this->author_number]['rss'], $data);
break;
}
if ($this->inside_author)
{
switch ($this->tag_name)
{
case 'NAME':
case $this->namespaces['atom'] . ':NAME':
$this->do_add_content($this->data['items'][$this->item_number]['author'][$this->author_number]['name'], $data);
break;
case 'URL':
case $this->namespaces['atom'] . ':URL':
$this->do_add_content($this->data['items'][$this->item_number]['author'][$this->author_number]['url'], $data);
break;
case 'URI':
case $this->namespaces['atom'] . ':URI':
$this->do_add_content($this->data['items'][$this->item_number]['author'][$this->author_number]['uri'], $data);
break;
case 'HOMEPAGE':
case $this->namespaces['atom'] . ':HOMEPAGE':
$this->do_add_content($this->data['items'][$this->item_number]['author'][$this->author_number]['homepage'], $data);
break;
case 'EMAIL':
case $this->namespaces['atom'] . ':EMAIL':
$this->do_add_content($this->data['items'][$this->item_number]['author'][$this->author_number]['email'], $data);
break;
}
}
}
else if (($this->inside_channel && !$this->inside_image) || (isset($this->data['feedinfo']['type']) && $this->data['feedinfo']['type'] == 'Atom'))
{
switch ($this->tag_name)
{
case 'TITLE':
case $this->namespaces['rss1'] . ':TITLE':
case $this->namespaces['rss2'] . ':TITLE':
case $this->namespaces['atom'] . ':TITLE':
$this->do_add_content($this->data['info']['title'], $data);
break;
case 'LINK':
case $this->namespaces['rss1'] . ':LINK':
case $this->namespaces['rss2'] . ':LINK':
case $this->namespaces['atom'] . ':LINK':
$this->do_add_content($this->data['info']['link'][$this->link_number], $data);
break;
case 'DESCRIPTION':
case $this->namespaces['rss1'] . ':DESCRIPTION':
case $this->namespaces['rss2'] . ':DESCRIPTION':
$this->do_add_content($this->data['info']['description'], $data);
break;
case $this->namespaces['dc'] . ':DESCRIPTION':
$this->do_add_content($this->data['info']['dc:description'], $data);
break;
case 'TAGLINE':
case $this->namespaces['atom'] . ':TAGLINE':
$this->do_add_content($this->data['info']['tagline'], $data);
break;
case 'SUBTITLE':
case $this->namespaces['atom'] . ':SUBTITLE':
$this->do_add_content($this->data['info']['subtitle'], $data);
break;
case 'COPYRIGHT':
case $this->namespaces['rss1'] . ':COPYRIGHT':
case $this->namespaces['rss2'] . ':COPYRIGHT':
case $this->namespaces['atom'] . ':COPYRIGHT':
$this->do_add_content($this->data['info']['copyright'], $data);
break;
case 'LANGUAGE':
case $this->namespaces['rss1'] . ':LANGUAGE':
case $this->namespaces['rss2'] . ':LANGUAGE':
$this->do_add_content($this->data['info']['language'], $data);
break;
case 'LOGO':
case $this->namespaces['atom'] . ':LOGO':
$this->do_add_content($this->data['info']['logo'], $data);
break;
}
}
else if ($this->inside_channel && $this->inside_image)
{
switch ($this->tag_name)
{
case 'TITLE':
case $this->namespaces['rss1'] . ':TITLE':
case $this->namespaces['rss2'] . ':TITLE':
$this->do_add_content($this->data['info']['image']['title'], $data);
break;
case 'URL':
case $this->namespaces['rss1'] . ':URL':
case $this->namespaces['rss2'] . ':URL':
$this->do_add_content($this->data['info']['image']['url'], $data);
break;
case 'LINK':
case $this->namespaces['rss1'] . ':LINK':
case $this->namespaces['rss2'] . ':LINK':
$this->do_add_content($this->data['info']['image']['link'], $data);
break;
case 'WIDTH':
case $this->namespaces['rss1'] . ':WIDTH':
case $this->namespaces['rss2'] . ':WIDTH':
$this->do_add_content($this->data['info']['image']['width'], $data);
break;
case 'HEIGHT':
case $this->namespaces['rss1'] . ':HEIGHT':
case $this->namespaces['rss2'] . ':HEIGHT':
$this->do_add_content($this->data['info']['image']['height'], $data);
break;
}
}
$this->is_first = false;
}
function end_handler($parser, $name)
{
$this->tag_name = '';
switch ($name)
{
case 'ITEM':
case $this->namespaces['rss1'] . ':ITEM':
case $this->namespaces['rss2'] . ':ITEM':
case 'ENTRY':
case $this->namespaces['atom'] . ':ENTRY':
$this->inside_item = false;
$this->item_number++;
$this->author_number = 0;
$this->category_number = 0;
$this->enclosure_number = 0;
$this->item_link_number = 0;
break;
case 'CHANNEL':
case $this->namespaces['rss1'] . ':CHANNEL':
case $this->namespaces['rss2'] . ':CHANNEL':
$this->inside_channel = false;
break;
case 'IMAGE':
case $this->namespaces['rss1'] . ':IMAGE':
case $this->namespaces['rss2'] . ':IMAGE':
$this->inside_image = false;
break;
case 'AUTHOR':
case $this->namespaces['rss1'] . ':AUTHOR':
case $this->namespaces['rss2'] . ':AUTHOR':
case $this->namespaces['atom'] . ':AUTHOR':
$this->author_number++;
$this->inside_author = false;
break;
case 'CATEGORY':
case $this->namespaces['rss1'] . ':CATEGORY':
case $this->namespaces['rss2'] . ':CATEGORY':
case $this->namespaces['atom'] . ':CATEGORY':
case $this->namespaces['dc'] . ':SUBJECT':
$this->category_number++;
break;
case 'ENCLOSURE':
case $this->namespaces['rss1'] . ':ENCLOSURE':
case $this->namespaces['rss2'] . ':ENCLOSURE':
$this->enclosure_number++;
break;
case 'LINK':
case $this->namespaces['rss1'] . ':LINK':
case $this->namespaces['rss2'] . ':LINK':
case $this->namespaces['atom'] . ':LINK':
if ($this->inside_item)
{
$this->item_link_number++;
}
else
{
$this->link_number++;
}
break;
}
}
function start_name_space($parser, $prefix, $uri = null)
{
$prefix = strtoupper($prefix);
$uri = strtoupper($uri);
if ($prefix == 'ATOM' || $uri == 'HTTP://WWW.W3.ORG/2005/ATOM' || $uri == 'HTTP://PURL.ORG/ATOM/NS#')
{
$this->namespaces['atom'] = $uri;
}
else if ($prefix == 'RSS2' || $uri == 'HTTP://BACKEND.USERLAND.COM/RSS2')
{
$this->namespaces['rss2'] = $uri;
}
else if ($prefix == 'RDF' || $uri == 'HTTP://WWW.W3.ORG/1999/02/22-RDF-SYNTAX-NS#')
{
$this->namespaces['rdf'] = $uri;
}
else if ($prefix == 'RSS' || $uri == 'HTTP://PURL.ORG/RSS/1.0/' || $uri == 'HTTP://MY.NETSCAPE.COM/RDF/SIMPLE/0.9/')
{
$this->namespaces['rss1'] = $uri;
}
else if ($prefix == 'DC' || $uri == 'HTTP://PURL.ORG/DC/ELEMENTS/1.1/')
{
$this->namespaces['dc'] = $uri;
}
else if ($prefix == 'XHTML' || $uri == 'HTTP://WWW.W3.ORG/1999/XHTML')
{
$this->namespaces['xhtml'] = $uri;
$this->xhtml_prefix = $prefix;
}
else if ($prefix == 'CONTENT' || $uri == 'HTTP://PURL.ORG/RSS/1.0/MODULES/CONTENT/')
{
$this->namespaces['content'] = $uri;
}
}
function end_name_space($parser, $prefix)
{
if ($key = array_search(strtoupper($prefix), $this->namespaces))
{
if ($key == 'atom')
{
$this->namespaces['atom'] = 'ATOM';
}
else if ($key == 'rss2')
{
$this->namespaces['rss2'] = 'RSS';
}
else if ($key == 'rdf')
{
$this->namespaces['rdf'] = 'RDF';
}
else if ($key == 'rss1')
{
$this->namespaces['rss1'] = 'RSS';
}
else if ($key == 'dc')
{
$this->namespaces['dc'] = 'DC';
}
else if ($key == 'xhtml')
{
$this->namespaces['xhtml'] = 'XHTML';
$this->xhtml_prefix = 'XHTML';
}
else if ($key == 'content')
{
$this->namespaces['content'] = 'CONTENT';
}
}
}
}
/**
* @package sapphire
* @subpackage integration
*/
class SimplePie_Sanitize
{
// Private vars
var $feedinfo;
var $info;
var $items;
var $feed_xmlbase;
var $item_xmlbase;
var $attribs;
var $cached_entities;
var $cache_convert_entities;
// Options
var $remove_div = true;
var $strip_ads = false;
var $replace_headers = false;
var $bypass_image_hotlink = false;
var $bypass_image_hotlink_page = false;
var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
var $encode_instead_of_strip = false;
var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur');
var $input_encoding = 'UTF-8';
var $output_encoding = 'UTF-8';
var $item_class = 'SimplePie_Item';
var $author_class = 'SimplePie_Author';
var $enclosure_class = 'SimplePie_Enclosure';
function remove_div($enable = true)
{
$this->remove_div = (bool) $enable;
}
function strip_ads($enable = false)
{
$this->strip_ads = (bool) $enable;
}
function replace_headers($enable = false)
{
$this->enable_headers = (bool) $enable;
}
function bypass_image_hotlink($get = false)
{
if ($get)
{
$this->bypass_image_hotlink = (string) $get;
}
else
{
$this->bypass_image_hotlink = false;
}
}
function bypass_image_hotlink_page($page = false)
{
if ($page)
{
$this->bypass_image_hotlink_page = (string) $page;
}
else
{
$this->bypass_image_hotlink_page = false;
}
}
function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'))
{
if ($tags)
{
if (is_array($tags))
{
$this->strip_htmltags = $tags;
}
else
{
$this->strip_htmltags = explode(',', $tags);
}
}
else
{
$this->strip_htmltags = false;
}
}
function encode_instead_of_strip($enable = false)
{
$this->encode_instead_of_strip = (bool) $enable;
}
function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur'))
{
if ($attribs)
{
if (is_array($attribs))
{
$this->strip_attributes = $attribs;
}
else
{
$this->strip_attributes = explode(',', $attribs);
}
}
else
{
$this->strip_attributes = false;
}
}
function input_encoding($encoding = 'UTF-8')
{
$this->input_encoding = (string) $encoding;
}
function output_encoding($encoding = 'UTF-8')
{
$this->output_encoding = (string) $encoding;
}
function set_item_class($class = 'SimplePie_Item')
{
if (SimplePie_Misc::is_a_class($class, 'SimplePie_Item'))
{
$this->item_class = $class;
return true;
}
return false;
}
function set_author_class($class = 'SimplePie_Author')
{
if (SimplePie_Misc::is_a_class($class, 'SimplePie_Author'))
{
$this->author_class = $class;
return true;
}
return false;
}
function set_enclosure_class($class = 'SimplePie_Enclosure')
{
if (SimplePie_Misc::is_a_class($class, 'SimplePie_Enclosure'))
{
$this->enclosure_class = $class;
return true;
}
return false;
}
function parse_data_array(&$data, $url)
{
// Feed Info (Type and Version)
if (!empty($data['feedinfo']['type']))
{
$this->feedinfo = $data['feedinfo'];
}
// Feed level xml:base
if (!empty($data['feeddata']['attribs']['XML:BASE']))
{
$this->feed_xmlbase = $data['feeddata']['attribs']['XML:BASE'];
}
else if (!empty($data['feeddata']['attribs']['HTTP://WWW.W3.ORG/XML/1998/NAMESPACE:BASE']))
{
$this->feed_xmlbase = $data['feeddata']['attribs']['HTTP://WWW.W3.ORG/XML/1998/NAMESPACE:BASE'];
}
// FeedBurner feeds use alternate link
else if (strpos($url, 'http://feeds.feedburner.com/') !== 0)
{
$this->feed_xmlbase = SimplePie_Misc::parse_url($url);
if (empty($this->feed_xmlbase['authority']))
{
$this->feed_xmlbase = preg_replace('/^' . preg_quote(realpath($_SERVER['DOCUMENT_ROOT']), '/') . '/', '', realpath($url));
}
else
{
$this->feed_xmlbase = $url;
}
}
// Feed link(s)
if (!empty($data['info']['link']))
{
foreach ($data['info']['link'] as $link)
{
if (empty($link['attribs']['REL']))
{
$rel = 'alternate';
}
else
{
$rel = strtolower($link['attribs']['REL']);
}
if ($rel == 'enclosure')
{
$href = null;
$type = null;
$length = null;
if (!empty($link['data']))
{
$href = $this->sanitize($link['data'], $link['attribs'], true);
}
else if (!empty($link['attribs']['HREF']))
{
$href = $this->sanitize($link['attribs']['HREF'], $link['attribs'], true);
}
if (!empty($link['attribs']['TYPE'])) {
$type = $this->sanitize($link['attribs']['TYPE'], $link['attribs']);
}
if (!empty($link['attribs']['LENGTH'])) {
$length = $this->sanitize($link['attribs']['LENGTH'], $link['attribs']);
}
$this->info['link']['enclosure'][] = new $this->enclosure_class($href, $type, $length);
}
else
{
if (!empty($link['data']))
{
$this->info['link'][$rel][] = $this->sanitize($link['data'], $link['attribs'], true);
}
else if (!empty($link['attribs']['HREF']))
{
$this->info['link'][$rel][] = $this->sanitize($link['attribs']['HREF'], $link['attribs'], true);
}
}
}
}
// Use the first alternate link if we don't have any feed xml:base
if (empty($this->feed_xmlbase) && !empty($this->info['link']['alternate'][0]))
{
$this->feed_xmlbase = $this->info['link']['alternate'][0];
}
// Feed Title
if (!empty($data['info']['title']['data']))
{
$this->info['title'] = $this->sanitize($data['info']['title']['data'], $data['info']['title']['attribs']);
}
// Feed Descriptions
if (!empty($data['info']['description']['data']))
{
$this->info['description'] = $this->sanitize($data['info']['description']['data'], $data['info']['description']['attribs'], false, true);
}
if (!empty($data['info']['dc:description']['data']))
{
$this->info['dc:description'] = $this->sanitize($data['info']['dc:description']['data'], $data['info']['dc:description']['attribs']);
}
if (!empty($data['info']['tagline']['data']))
{
$this->info['tagline'] = $this->sanitize($data['info']['tagline']['data'], $data['info']['tagline']['attribs']);
}
if (!empty($data['info']['subtitle']['data']))
{
$this->info['subtitle'] = $this->sanitize($data['info']['subtitle']['data'], $data['info']['subtitle']['attribs']);
}
// Feed Language
if (!empty($data['info']['language']['data']))
{
$this->info['language'] = $this->sanitize($data['info']['language']['data'], $data['info']['language']['attribs']);
}
if (!empty($data['feeddata']['attribs']['XML:LANG']))
{
$this->info['xml:lang'] = $this->sanitize($data['feeddata']['attribs']['XML:LANG'], null);
}
else if (!empty($data['feeddata']['attribs']['HTTP://WWW.W3.ORG/XML/1998/NAMESPACE:LANG']))
{
$this->info['xml:lang'] = $this->sanitize($data['feeddata']['attribs']['HTTP://WWW.W3.ORG/XML/1998/NAMESPACE:LANG'], null);
}
// Feed Copyright
if (!empty($data['info']['copyright']['data']))
{
$this->info['copyright'] = $this->sanitize($data['info']['copyright']['data'], $data['info']['copyright']['attribs']);
}
// Feed Image
if (!empty($data['info']['image']['title']['data']))
{
$this->info['image']['title'] = $this->sanitize($data['info']['image']['title']['data'], $data['info']['image']['title']['attribs']);
}
if (!empty($data['info']['image']['url']['data']))
{
$this->info['image']['url'] = $this->sanitize($data['info']['image']['url']['data'], $data['info']['image']['url']['attribs'], true);
}
if (!empty($data['info']['logo']['data']))
{
$this->info['image']['logo'] = $this->sanitize($data['info']['logo']['data'], $data['info']['logo']['attribs'], true);
}
if (!empty($data['info']['image']['link']['data']))
{
$this->info['image']['link'] = $this->sanitize($data['info']['image']['link']['data'], $data['info']['image']['link']['attribs'], true);
}
if (!empty($data['info']['image']['width']['data']))
{
$this->info['image']['width'] = $this->sanitize($data['info']['image']['width']['data'], $data['info']['image']['width']['attribs']);
}
if (!empty($data['info']['image']['height']['data']))
{
$this->info['image']['height'] = $this->sanitize($data['info']['image']['height']['data'], $data['info']['image']['height']['attribs']);
}
// Items
if (!empty($data['items']))
{
foreach ($data['items'] as $key => $item)
{
$newitem = null;
// Item level xml:base
if (!empty($item['attribs']['XML:BASE']))
{
$this->item_xmlbase = SimplePie_Misc::absolutize_url($item['attribs']['XML:BASE'], $this->feed_xmlbase);
}
else if (!empty($item['attribs']['HTTP://WWW.W3.ORG/XML/1998/NAMESPACE:BASE']))
{
$this->item_xmlbase = SimplePie_Misc::absolutize_url($item['attribs']['HTTP://WWW.W3.ORG/XML/1998/NAMESPACE:BASE'], $this->feed_xmlbase);
}
else
{
$this->item_xmlbase = null;
}
// Title
if (!empty($item['title']['data'])) {
$newitem['title'] = $this->sanitize($item['title']['data'], $item['title']['attribs']);
}
if (!empty($item['dc:title']['data']))
{
$newitem['dc:title'] = $this->sanitize($item['dc:title']['data'], $item['dc:title']['attribs']);
}
// Description
if (!empty($item['content']['data']))
{
$newitem['content'] = $this->sanitize($item['content']['data'], $item['content']['attribs']);
}
if (!empty($item['encoded']['data']))
{
$newitem['encoded'] = $this->sanitize($item['encoded']['data'], $item['encoded']['attribs']);
}
if (!empty($item['summary']['data']))
{
$newitem['summary'] = $this->sanitize($item['summary']['data'], $item['summary']['attribs']);
}
if (!empty($item['description']['data']))
{
$newitem['description'] = $this->sanitize($item['description']['data'], $item['description']['attribs'], false, true);
}
if (!empty($item['dc:description']['data']))
{
$newitem['dc:description'] = $this->sanitize($item['dc:description']['data'], $item['dc:description']['attribs']);
}
if (!empty($item['longdesc']['data']))
{
$newitem['longdesc'] = $this->sanitize($item['longdesc']['data'], $item['longdesc']['attribs']);
}
// Link(s)
if (!empty($item['link']))
{
foreach ($item['link'] as $link)
{
if (empty($link['attribs']['REL']))
{
$rel = 'alternate';
}
else
{
$rel = strtolower($link['attribs']['REL']);
}
if ($rel == 'enclosure')
{
$href = null;
$type = null;
$length = null;
if (!empty($link['data']))
{
$href = $this->sanitize($link['data'], $link['attribs'], true);
}
else if (!empty($link['attribs']['HREF']))
{
$href = $this->sanitize($link['attribs']['HREF'], $link['attribs'], true);
}
if (!empty($link['attribs']['TYPE'])) {
$type = $this->sanitize($link['attribs']['TYPE'], $link['attribs']);
}
if (!empty($link['attribs']['LENGTH'])) {
$length = $this->sanitize($link['attribs']['LENGTH'], $link['attribs']);
}
if (!empty($href))
{
$newitem['link'][$rel][] = new $this->enclosure_class($href, $type, $length);
}
}
else
{
if (!empty($link['data']))
{
$newitem['link'][$rel][] = $this->sanitize($link['data'], $link['attribs'], true);
}
else if (!empty($link['attribs']['HREF']))
{
$newitem['link'][$rel][] = $this->sanitize($link['attribs']['HREF'], $link['attribs'], true);
}
}
}
}
// Enclosure(s)
if (!empty($item['enclosure']))
{
foreach ($item['enclosure'] as $enclosure)
{
if (!empty($enclosure['attribs']['URL']))
{
$type = null;
$length = null;
$href = $this->sanitize($enclosure['attribs']['URL'], $enclosure['attribs'], true);
if (!empty($enclosure['attribs']['TYPE']))
{
$type = $this->sanitize($enclosure['attribs']['TYPE'], $enclosure['attribs']);
}
if (!empty($enclosure['attribs']['LENGTH']))
{
$length = $this->sanitize($enclosure['attribs']['LENGTH'], $enclosure['attribs']);
}
$newitem['enclosures'][] = new $this->enclosure_class($href, $type, $length);
}
}
}
// ID
if (!empty($item['guid']['data']))
{
if (!empty($item['guid']['attribs']['ISPERMALINK']) && strtolower($item['guid']['attribs']['ISPERMALINK']) == 'false')
{
$newitem['guid']['permalink'] = false;
}
else
{
$newitem['guid']['permalink'] = true;
}
$newitem['guid']['data'] = $this->sanitize($item['guid']['data'], $item['guid']['attribs']);
}
if (!empty($item['id']['data']))
{
$newitem['id'] = $this->sanitize($item['id']['data'], $item['id']['attribs']);
}
// Date
if (!empty($item['pubdate']['data']))
{
$newitem['pubdate'] = $this->parse_date($this->sanitize($item['pubdate']['data'], $item['pubdate']['attribs']));
}
if (!empty($item['dc:date']['data']))
{
$newitem['dc:date'] = $this->parse_date($this->sanitize($item['dc:date']['data'], $item['dc:date']['attribs']));
}
if (!empty($item['issued']['data']))
{
$newitem['issued'] = $this->parse_date($this->sanitize($item['issued']['data'], $item['issued']['attribs']));
}
if (!empty($item['published']['data']))
{
$newitem['published'] = $this->parse_date($this->sanitize($item['published']['data'], $item['published']['attribs']));
}
if (!empty($item['modified']['data']))
{
$newitem['modified'] = $this->parse_date($this->sanitize($item['modified']['data'], $item['modified']['attribs']));
}
if (!empty($item['updated']['data']))
{
$newitem['updated'] = $this->parse_date($this->sanitize($item['updated']['data'], $item['updated']['attribs']));
}
// Categories
if (!empty($item['category']))
{
foreach ($item['category'] as $category)
{
if (!empty($category['data']))
{
$newitem['category'][] = $this->sanitize($category['data'], $category['attribs']);
}
else if (!empty($category['attribs']['TERM']))
{
$newitem['term'][] = $this->sanitize($category['attribs']['TERM'], $category['attribs']);
}
}
}
if (!empty($item['subject']))
{
foreach ($item['subject'] as $category)
{
if (!empty($category['data']))
{
$newitem['subject'][] = $this->sanitize($category['data'], $category['attribs']);
}
}
}
// Author
if (!empty($item['creator']))
{
foreach ($item['creator'] as $creator)
{
if (!empty($creator['data']))
{
$newitem['creator'][] = new $this->author_class($this->sanitize($creator['data'], $creator['attribs']), null, null);
}
}
}
if (!empty($item['author']))
{
foreach ($item['author'] as $author)
{
$name = null;
$link = null;
$email = null;
if (!empty($author['rss']))
{
$sane = $this->sanitize($author['rss']['data'], $author['rss']['attribs']);
if (preg_match('/(.*)@(.*) \((.*)\)/msiU', $sane, $matches)) {
$name = trim($matches[3]);
$email = trim("$matches[1]@$matches[2]");
} else {
$email = $sane;
}
}
else
{
if (!empty($author['name']))
{
$name = $this->sanitize($author['name']['data'], $author['name']['attribs']);
}
if (!empty($author['url']))
{
$link = $this->sanitize($author['url']['data'], $author['url']['attribs'], true);
}
else if (!empty($author['uri']))
{
$link = $this->sanitize($author['uri']['data'], $author['uri']['attribs'], true);
}
else if (!empty($author['homepage']))
{
$link = $this->sanitize($author['homepage']['data'], $author['homepage']['attribs'], true);
}
if (!empty($author['email'])) {
$email = $this->sanitize($author['email']['data'], $author['email']['attribs']);
}
}
$newitem['author'][] = new $this->author_class($name, $link, $email);
}
}
unset($data['items'][$key]);
$this->items[] = new $this->item_class($newitem);
}
}
}
function sanitize($data, $attribs, $is_url = false, $force_decode = false)
{
$this->attribs = $attribs;
if (isset($this->feedinfo['type']) && $this->feedinfo['type'] == 'Atom')
{
if ((!empty($attribs['MODE']) && $attribs['MODE'] == 'base64') || (!empty($attribs['TYPE']) && $attribs['TYPE'] == 'application/octet-stream'))
{
$data = trim($data);
$data = base64_decode($data);
}
else if ((!empty($attribs['MODE']) && $attribs['MODE'] == 'escaped' || !empty($attribs['TYPE']) && ($attribs['TYPE'] == 'html' || $attribs['TYPE'] == 'text/html')))
{
$data = $this->entities_decode($data);
}
if (!empty($attribs['TYPE']) && ($attribs['TYPE'] == 'xhtml' || $attribs['TYPE'] == 'application/xhtml+xml'))
{
if ($this->remove_div)
{
$data = preg_replace('/<div( .*)?>/msiU', '', strrev(preg_replace('/>vid\/</i', '', strrev($data), 1)), 1);
}
else
{
$data = preg_replace('/<div( .*)?>/msiU', '<div>', $data, 1);
}
$data = $this->convert_entities($data);
}
}
else
{
$data = $this->convert_entities($data);
}
if ($force_decode)
{
$data = $this->entities_decode($data);
}
$data = trim($data);
$data = preg_replace('/<\!--([^-]|-[^-])*-->/msiU', '', $data);
// If Strip Ads is enabled, strip them.
if ($this->strip_ads)
{
$data = preg_replace('/<a (.*)href=(.*)click\.phdo\?s=(.*)<\/a>/msiU', '', $data); // Pheedo links (tested with Dooce.com)
$data = preg_replace('/<p(.*)>(.*)<a href="http:\/\/ad.doubleclick.net\/jump\/(.*)<\/p>/msiU', '', $data); // Doubleclick links (tested with InfoWorld.com)
$data = preg_replace('/<p><map (.*)name=(.*)google_ad_map(.*)<\/p>/msiU', '', $data); // Google AdSense for Feeds (tested with tuaw.com).
// Feedflare, from Feedburner
}
// Replace H1, H2, and H3 tags with the less important H4 tags.
// This is because on a site, the more important headers might make sense,
// but it most likely doesn't fit in the context of RSS-in-a-webpage.
if ($this->replace_headers)
{
$data = preg_replace('/<h[1-3]((\s*((\w+:)?\w+)\s*=\s*("([^"]*)"|\'([^\']*)\'|(.*)))*)\s*>/msiU', '<h4\\1>', $data);
$data = preg_replace('/<\/h[1-3]>/i', '</h4>', $data);
}
if ($is_url)
{
$data = $this->replace_urls($data, true);
}
else
{
$data = preg_replace_callback('/<(\S+)((\s*((\w+:)?\w+)\s*=\s*("([^"]*)"|\'([^\']*)\'|(.*)))*)\s*(\/>|>(.*)<\/\S+>)/msiU', array(&$this, 'replace_urls'), $data);
}
// If Bypass Image Hotlink is enabled, rewrite all the image tags.
if ($this->bypass_image_hotlink)
{
$images = SimplePie_Misc::get_element('img', $data);
foreach ($images as $img)
{
if (!empty($img['attribs']['SRC']['data']))
{
$pre = '';
if ($this->bypass_image_hotlink_page)
{
$pre = $this->bypass_image_hotlink_page;
}
$pre .= "?$this->bypass_image_hotlink=";
$img['attribs']['SRC']['data'] = $pre . rawurlencode(strtr($img['attribs']['SRC']['data'], array_flip(get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES))));
$data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data);
}
}
}
// Strip out HTML tags and attributes that might cause various security problems.
// Based on recommendations by Mark Pilgrim at:
// http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
if ($this->strip_htmltags)
{
foreach ($this->strip_htmltags as $tag)
{
$data = preg_replace_callback("/<($tag)((\s*((\w+:)?\w+)(\s*=\s*(\"([^\"]*)\"|'([^']*)'|(.*)))?)*)\s*(\/>|>(.*)<\/($tag)((\s*((\w+:)?\w+)(\s*=\s*(\"([^\"]*)\"|'([^']*)'|(.*)))?)*)\s*>)/msiU", array(&$this, 'do_strip_htmltags'), $data);
}
}
if ($this->strip_attributes)
{
foreach ($this->strip_attributes as $attrib)
{
$data = preg_replace('/ '. trim($attrib) .'=("|&quot;)(\w|\s|=|-|:|;|\/|\.|\?|&|,|#|!|\(|\)|\'|&apos;|<|>|\+|{|})*("|&quot;)/i', '', $data);
$data = preg_replace('/ '. trim($attrib) .'=(\'|&apos;)(\w|\s|=|-|:|;|\/|\.|\?|&|,|#|!|\(|\)|"|&quot;|<|>|\+|{|})*(\'|&apos;)/i', '', $data);
$data = preg_replace('/ '. trim($attrib) .'=(\w|\s|=|-|:|;|\/|\.|\?|&|,|#|!|\(|\)|\+|{|})*/i', '', $data);
}
}
// Convert encoding
$data = SimplePie_Misc::change_encoding($data, $this->input_encoding, $this->output_encoding);
return $data;
}
function do_strip_htmltags($match)
{
if ($this->encode_instead_of_strip)
{
if (isset($match[12]) && !in_array(strtolower($match[1]), array('script', 'style')))
{
return "&lt;$match[1]$match[2]&gt;$match[12]&lt;/$match[1]&gt;";
}
else if (isset($match[12]))
{
return "&lt;$match[1]$match[2]&gt;&lt;/$match[1]&gt;";
}
else
{
return "&lt;$match[1]$match[2]/&gt;";
}
}
else
{
if (isset($match[12]) && !in_array(strtolower($match[1]), array('script', 'style')))
{
return $match[12];
}
else
{
return '';
}
}
}
function replace_urls($data, $raw_url = false)
{
if (!empty($this->attribs['XML:BASE']))
{
$xmlbase = $attribs['XML:BASE'];
}
else if (!empty($this->attribs['HTTP://WWW.W3.ORG/XML/1998/NAMESPACE:BASE']))
{
$xmlbase = $this->attribs['HTTP://WWW.W3.ORG/XML/1998/NAMESPACE:BASE'];
}
if (!empty($xmlbase))
{
if (!empty($this->item_xmlbase))
{
$xmlbase = SimplePie_Misc::absolutize_url($xmlbase, $this->item_xmlbase);
}
else
{
$xmlbase = SimplePie_Misc::absolutize_url($xmlbase, $this->feed_xmlbase);
}
}
else if (!empty($this->item_xmlbase))
{
$xmlbase = $this->item_xmlbase;
}
else
{
$xmlbase = $this->feed_xmlbase;
}
if ($raw_url)
{
return SimplePie_Misc::absolutize_url($data, $xmlbase);
}
else
{
$attributes = array(
'background',
'href',
'src',
'longdesc',
'usemap',
'codebase',
'data',
'classid',
'cite',
'action',
'profile',
'for'
);
foreach ($attributes as $attribute)
{
if (preg_match("/$attribute='(.*)'/siU", $data[0], $attrib) || preg_match("/$attribute=\"(.*)\"/siU", $data[0], $attrib) || preg_match("/$attribute=(.*)[ |\/|>]/siU", $data[0], $attrib))
{
$new_tag = str_replace($attrib[1], SimplePie_Misc::absolutize_url($attrib[1], $xmlbase), $attrib[0]);
$data[0] = str_replace($attrib[0], $new_tag, $data[0]);
}
}
return $data[0];
}
}
function entities_decode($data)
{
return preg_replace_callback('/&(#)?(x)?([0-9a-z]+);/mi', array(&$this, 'do_entites_decode'), $data);
}
function do_entites_decode($data)
{
if (isset($this->cached_entities[$data[0]]))
{
return $this->cached_entities[$data[0]];
}
else
{
$return = SimplePie_Misc::change_encoding(html_entity_decode($data[0], ENT_QUOTES), 'ISO-8859-1', $this->input_encoding);
if ($return == $data[0])
{
$return = SimplePie_Misc::change_encoding(preg_replace_callback('/&#([x]?[0-9a-f]+);/mi', array(&$this, 'replace_num_entity'), $data[0]), 'UTF-8', $this->input_encoding);
}
$this->cached_entities[$data[0]] = $return;
return $return;
}
}
function convert_entities($data)
{
return preg_replace_callback('/&#(x)?([0-9a-z]+);/mi', array(&$this, 'do_convert_entities'), $data);
}
function do_convert_entities($data)
{
if (isset($this->cache_convert_entities[$data[0]]))
{
return $this->cache_convert_entities[$data[0]];
}
else if (isset($this->cached_entities[$data[0]]))
{
$return = htmlentities($this->cached_entities[$data[0]], ENT_QUOTES, 'UTF-8');
}
else
{
$return = htmlentities(preg_replace_callback('/&#([x]?[0-9a-f]+);/mi', array(&$this, 'replace_num_entity'), $data[0]), ENT_QUOTES, 'UTF-8');
}
$this->cache_convert_entities[$data[0]] = $return;
return $return;
}
/*
* Escape numeric entities
* From a PHP Manual note (on html_entity_decode())
* Copyright (c) 2005 by "php dot net at c dash ovidiu dot tk",
* "emilianomartinezluque at yahoo dot com" and "hurricane at cyberworldz dot org".
*
* This material may be distributed only subject to the terms and conditions set forth in
* the Open Publication License, v1.0 or later (the latest version is presently available at
* http://www.opencontent.org/openpub/).
*/
function replace_num_entity($ord)
{
$ord = $ord[1];
if (preg_match('/^x([0-9a-f]+)$/i', $ord, $match))
{
$ord = hexdec($match[1]);
}
else
{
$ord = intval($ord);
}
$no_bytes = 0;
$byte = array();
if ($ord < 128)
{
return chr($ord);
}
if ($ord < 2048)
{
$no_bytes = 2;
}
else if ($ord < 65536)
{
$no_bytes = 3;
}
else if ($ord < 1114112)
{
$no_bytes = 4;
}
else
{
return;
}
switch ($no_bytes)
{
case 2:
$prefix = array(31, 192);
break;
case 3:
$prefix = array(15, 224);
break;
case 4:
$prefix = array(7, 240);
break;
}
for ($i = 0; $i < $no_bytes; $i++)
{
$byte[$no_bytes-$i-1] = (($ord & (63 * pow(2,6*$i))) / pow(2,6*$i)) & 63 | 128;
}
$byte[0] = ($byte[0] & $prefix[0]) | $prefix[1];
$ret = '';
for ($i = 0; $i < $no_bytes; $i++)
{
$ret .= chr($byte[$i]);
}
return $ret;
}
function parse_date($date)
{
$military_timezone = array('A' => '-0100', 'B' => '-0200', 'C' => '-0300', 'D' => '-0400', 'E' => '-0500', 'F' => '-0600', 'G' => '-0700', 'H' => '-0800', 'I' => '-0900', 'K' => '-1000', 'L' => '-1100', 'M' => '-1200', 'N' => '+0100', 'O' => '+0200', 'P' => '+0300', 'Q' => '+0400', 'R' => '+0500', 'S' => '+0600', 'T' => '+0700', 'U' => '+0800', 'V' => '+0900', 'W' => '+1000', 'X' => '+1100', 'Y' => '+1200', 'Z' => '-0000');
$north_american_timezone = array('GMT' => '-0000', 'EST' => '-0500', 'EDT' => '-0400', 'CST' => '-0600', 'CDT' => '-0500', 'MST' => '-0700', 'MDT' => '-0600', 'PST' => '-0800', 'PDT' => '-0700');
if (preg_match('/([0-9]{2,4})-?([0-9]{2})-?([0-9]{2})T([0-9]{2}):?([0-9]{2})(:?([0-9]{2}(\.[0-9]*)?))?(UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[a-z]|(\\+|-)[0-9]{4}|(\\+|-)[0-9]{2}:[0-9]{2})?/i', $date, $matches))
{
if (!isset($matches[7]))
{
$matches[7] = '';
}
if (!isset($matches[9]))
{
$matches[9] = '';
}
$matches[7] = str_pad(round($matches[7]), 2, '0', STR_PAD_LEFT);
switch (strlen($matches[9]))
{
case 0:
$timezone = '';
break;
case 1:
$timezone = $military_timezone[strtoupper($matches[9])];
break;
case 2:
$timezone = '-0000';
break;
case 3:
$timezone = $north_american_timezone[strtoupper($matches[9])];
break;
case 5:
$timezone = $matches[9];
break;
case 6:
$timezone = substr_replace($matches[9], '', 3, 1);
break;
}
$date = strtotime("$matches[1]-$matches[2]-$matches[3] $matches[4]:$matches[5]:$matches[7] $timezone");
}
else if (preg_match('/([0-9]{1,2})\s*(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s*([0-9]{2}|[0-9]{4})\s*([0-9]{2}):([0-9]{2})(:([0-9]{2}(\.[0-9]*)?))?\s*(UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[a-z]|(\\+|-)[0-9]{4}|(\\+|-)[0-9]{2}:[0-9]{2})?/i', $date, $matches))
{
$three_month = array('Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4, 'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9, 'Oct' => 10, 'Nov' => 11, 'Dec' => 12);
$month = $three_month[$matches[2]];
if (strlen($matches[3]) == 2)
{
$year = ($matches[3] < 70) ? "20$matches[3]" : "19$matches[3]";
}
else
{
$year = $matches[3];
}
if (!isset($matches[7]))
{
$matches[7] = '';
}
if (!isset($matches[9]))
{
$matches[9] = '';
}
$second = str_pad(round($matches[7]), 2, '0', STR_PAD_LEFT);
switch (strlen($matches[9]))
{
case 0:
$timezone = '';
break;
case 1:
$timezone = $military_timezone[strtoupper($matches[9])];
break;
case 2:
$timezone = '-0000';
break;
case 3:
$timezone = $north_american_timezone[strtoupper($matches[9])];
break;
case 5:
$timezone = $matches[9];
break;
case 6:
$timezone = substr_replace($matches[9], '', 3, 1);
break;
}
$date = strtotime("$year-$month-$matches[1] $matches[4]:$matches[5]:$second $timezone");
}
else
{
$date = strtotime($date);
}
if ($date !== false && $date !== -1)
{
return $date;
}
else
{
return false;
}
}
}
?>

502
thirdparty/simpletest/LICENSE vendored Normal file
View File

@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

1
thirdparty/simpletest/VERSION vendored Normal file
View File

@ -0,0 +1 @@
1.0.1

View File

173
thirdparty/simpletest/compatibility.php vendored Normal file
View File

@ -0,0 +1,173 @@
<?php
/**
* base include file for SimpleTest
* @package SimpleTest
* @version $Id: compatibility.php 1723 2008-04-08 00:34:10Z lastcraft $
*/
/**
* Static methods for compatibility between different
* PHP versions.
* @package SimpleTest
*/
class SimpleTestCompatibility {
/**
* Creates a copy whether in PHP5 or PHP4.
* @param object $object Thing to copy.
* @return object A copy.
* @access public
* @static
*/
function copy($object) {
if (version_compare(phpversion(), '5') >= 0) {
eval('$copy = clone $object;');
return $copy;
}
return $object;
}
/**
* Identity test. Drops back to equality + types for PHP5
* objects as the === operator counts as the
* stronger reference constraint.
* @param mixed $first Test subject.
* @param mixed $second Comparison object.
* @return boolean True if identical.
* @access public
* @static
*/
function isIdentical($first, $second) {
if (version_compare(phpversion(), '5') >= 0) {
return SimpleTestCompatibility::_isIdenticalType($first, $second);
}
if ($first != $second) {
return false;
}
return ($first === $second);
}
/**
* Recursive type test.
* @param mixed $first Test subject.
* @param mixed $second Comparison object.
* @return boolean True if same type.
* @access private
* @static
*/
function _isIdenticalType($first, $second) {
if (gettype($first) != gettype($second)) {
return false;
}
if (is_object($first) && is_object($second)) {
if (get_class($first) != get_class($second)) {
return false;
}
return SimpleTestCompatibility::_isArrayOfIdenticalTypes(
get_object_vars($first),
get_object_vars($second));
}
if (is_array($first) && is_array($second)) {
return SimpleTestCompatibility::_isArrayOfIdenticalTypes($first, $second);
}
if ($first !== $second) {
return false;
}
return true;
}
/**
* Recursive type test for each element of an array.
* @param mixed $first Test subject.
* @param mixed $second Comparison object.
* @return boolean True if identical.
* @access private
* @static
*/
function _isArrayOfIdenticalTypes($first, $second) {
if (array_keys($first) != array_keys($second)) {
return false;
}
foreach (array_keys($first) as $key) {
$is_identical = SimpleTestCompatibility::_isIdenticalType(
$first[$key],
$second[$key]);
if (! $is_identical) {
return false;
}
}
return true;
}
/**
* Test for two variables being aliases.
* @param mixed $first Test subject.
* @param mixed $second Comparison object.
* @return boolean True if same.
* @access public
* @static
*/
function isReference(&$first, &$second) {
if (version_compare(phpversion(), '5', '>=') && is_object($first)) {
return ($first === $second);
}
if (is_object($first) && is_object($second)) {
$id = uniqid("test");
$first->$id = true;
$is_ref = isset($second->$id);
unset($first->$id);
return $is_ref;
}
$temp = $first;
$first = uniqid("test");
$is_ref = ($first === $second);
$first = $temp;
return $is_ref;
}
/**
* Test to see if an object is a member of a
* class hiearchy.
* @param object $object Object to test.
* @param string $class Root name of hiearchy.
* @return boolean True if class in hiearchy.
* @access public
* @static
*/
function isA($object, $class) {
if (version_compare(phpversion(), '5') >= 0) {
if (! class_exists($class, false)) {
if (function_exists('interface_exists')) {
if (! interface_exists($class, false)) {
return false;
}
}
}
eval("\$is_a = \$object instanceof $class;");
return $is_a;
}
if (function_exists('is_a')) {
return is_a($object, $class);
}
return ((strtolower($class) == get_class($object))
or (is_subclass_of($object, $class)));
}
/**
* Sets a socket timeout for each chunk.
* @param resource $handle Socket handle.
* @param integer $timeout Limit in seconds.
* @access public
* @static
*/
function setTimeout($handle, $timeout) {
if (function_exists('stream_set_timeout')) {
stream_set_timeout($handle, $timeout, 0);
} elseif (function_exists('socket_set_timeout')) {
socket_set_timeout($handle, $timeout, 0);
} elseif (function_exists('set_socket_timeout')) {
set_socket_timeout($handle, $timeout, 0);
}
}
}
?>

380
thirdparty/simpletest/cookies.php vendored Normal file
View File

@ -0,0 +1,380 @@
<?php
/**
* Base include file for SimpleTest
* @package SimpleTest
* @subpackage WebTester
* @version $Id: cookies.php 1723 2008-04-08 00:34:10Z lastcraft $
*/
/**#@+
* include other SimpleTest class files
*/
require_once(dirname(__FILE__) . '/url.php');
/**#@-*/
/**
* Cookie data holder. Cookie rules are full of pretty
* arbitary stuff. I have used...
* http://wp.netscape.com/newsref/std/cookie_spec.html
* http://www.cookiecentral.com/faq/
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleCookie {
var $_host;
var $_name;
var $_value;
var $_path;
var $_expiry;
var $_is_secure;
/**
* Constructor. Sets the stored values.
* @param string $name Cookie key.
* @param string $value Value of cookie.
* @param string $path Cookie path if not host wide.
* @param string $expiry Expiry date as string.
* @param boolean $is_secure Currently ignored.
*/
function SimpleCookie($name, $value = false, $path = false, $expiry = false, $is_secure = false) {
$this->_host = false;
$this->_name = $name;
$this->_value = $value;
$this->_path = ($path ? $this->_fixPath($path) : "/");
$this->_expiry = false;
if (is_string($expiry)) {
$this->_expiry = strtotime($expiry);
} elseif (is_integer($expiry)) {
$this->_expiry = $expiry;
}
$this->_is_secure = $is_secure;
}
/**
* Sets the host. The cookie rules determine
* that the first two parts are taken for
* certain TLDs and three for others. If the
* new host does not match these rules then the
* call will fail.
* @param string $host New hostname.
* @return boolean True if hostname is valid.
* @access public
*/
function setHost($host) {
if ($host = $this->_truncateHost($host)) {
$this->_host = $host;
return true;
}
return false;
}
/**
* Accessor for the truncated host to which this
* cookie applies.
* @return string Truncated hostname.
* @access public
*/
function getHost() {
return $this->_host;
}
/**
* Test for a cookie being valid for a host name.
* @param string $host Host to test against.
* @return boolean True if the cookie would be valid
* here.
*/
function isValidHost($host) {
return ($this->_truncateHost($host) === $this->getHost());
}
/**
* Extracts just the domain part that determines a
* cookie's host validity.
* @param string $host Host name to truncate.
* @return string Domain or false on a bad host.
* @access private
*/
function _truncateHost($host) {
$tlds = SimpleUrl::getAllTopLevelDomains();
if (preg_match('/[a-z\-]+\.(' . $tlds . ')$/i', $host, $matches)) {
return $matches[0];
} elseif (preg_match('/[a-z\-]+\.[a-z\-]+\.[a-z\-]+$/i', $host, $matches)) {
return $matches[0];
}
return false;
}
/**
* Accessor for name.
* @return string Cookie key.
* @access public
*/
function getName() {
return $this->_name;
}
/**
* Accessor for value. A deleted cookie will
* have an empty string for this.
* @return string Cookie value.
* @access public
*/
function getValue() {
return $this->_value;
}
/**
* Accessor for path.
* @return string Valid cookie path.
* @access public
*/
function getPath() {
return $this->_path;
}
/**
* Tests a path to see if the cookie applies
* there. The test path must be longer or
* equal to the cookie path.
* @param string $path Path to test against.
* @return boolean True if cookie valid here.
* @access public
*/
function isValidPath($path) {
return (strncmp(
$this->_fixPath($path),
$this->getPath(),
strlen($this->getPath())) == 0);
}
/**
* Accessor for expiry.
* @return string Expiry string.
* @access public
*/
function getExpiry() {
if (! $this->_expiry) {
return false;
}
return gmdate("D, d M Y H:i:s", $this->_expiry) . " GMT";
}
/**
* Test to see if cookie is expired against
* the cookie format time or timestamp.
* Will give true for a session cookie.
* @param integer/string $now Time to test against. Result
* will be false if this time
* is later than the cookie expiry.
* Can be either a timestamp integer
* or a cookie format date.
* @access public
*/
function isExpired($now) {
if (! $this->_expiry) {
return true;
}
if (is_string($now)) {
$now = strtotime($now);
}
return ($this->_expiry < $now);
}
/**
* Ages the cookie by the specified number of
* seconds.
* @param integer $interval In seconds.
* @public
*/
function agePrematurely($interval) {
if ($this->_expiry) {
$this->_expiry -= $interval;
}
}
/**
* Accessor for the secure flag.
* @return boolean True if cookie needs SSL.
* @access public
*/
function isSecure() {
return $this->_is_secure;
}
/**
* Adds a trailing and leading slash to the path
* if missing.
* @param string $path Path to fix.
* @access private
*/
function _fixPath($path) {
if (substr($path, 0, 1) != '/') {
$path = '/' . $path;
}
if (substr($path, -1, 1) != '/') {
$path .= '/';
}
return $path;
}
}
/**
* Repository for cookies. This stuff is a
* tiny bit browser dependent.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleCookieJar {
var $_cookies;
/**
* Constructor. Jar starts empty.
* @access public
*/
function SimpleCookieJar() {
$this->_cookies = array();
}
/**
* Removes expired and temporary cookies as if
* the browser was closed and re-opened.
* @param string/integer $now Time to test expiry against.
* @access public
*/
function restartSession($date = false) {
$surviving_cookies = array();
for ($i = 0; $i < count($this->_cookies); $i++) {
if (! $this->_cookies[$i]->getValue()) {
continue;
}
if (! $this->_cookies[$i]->getExpiry()) {
continue;
}
if ($date && $this->_cookies[$i]->isExpired($date)) {
continue;
}
$surviving_cookies[] = $this->_cookies[$i];
}
$this->_cookies = $surviving_cookies;
}
/**
* Ages all cookies in the cookie jar.
* @param integer $interval The old session is moved
* into the past by this number
* of seconds. Cookies now over
* age will be removed.
* @access public
*/
function agePrematurely($interval) {
for ($i = 0; $i < count($this->_cookies); $i++) {
$this->_cookies[$i]->agePrematurely($interval);
}
}
/**
* Sets an additional cookie. If a cookie has
* the same name and path it is replaced.
* @param string $name Cookie key.
* @param string $value Value of cookie.
* @param string $host Host upon which the cookie is valid.
* @param string $path Cookie path if not host wide.
* @param string $expiry Expiry date.
* @access public
*/
function setCookie($name, $value, $host = false, $path = '/', $expiry = false) {
$cookie = new SimpleCookie($name, $value, $path, $expiry);
if ($host) {
$cookie->setHost($host);
}
$this->_cookies[$this->_findFirstMatch($cookie)] = $cookie;
}
/**
* Finds a matching cookie to write over or the
* first empty slot if none.
* @param SimpleCookie $cookie Cookie to write into jar.
* @return integer Available slot.
* @access private
*/
function _findFirstMatch($cookie) {
for ($i = 0; $i < count($this->_cookies); $i++) {
$is_match = $this->_isMatch(
$cookie,
$this->_cookies[$i]->getHost(),
$this->_cookies[$i]->getPath(),
$this->_cookies[$i]->getName());
if ($is_match) {
return $i;
}
}
return count($this->_cookies);
}
/**
* Reads the most specific cookie value from the
* browser cookies. Looks for the longest path that
* matches.
* @param string $host Host to search.
* @param string $path Applicable path.
* @param string $name Name of cookie to read.
* @return string False if not present, else the
* value as a string.
* @access public
*/
function getCookieValue($host, $path, $name) {
$longest_path = '';
foreach ($this->_cookies as $cookie) {
if ($this->_isMatch($cookie, $host, $path, $name)) {
if (strlen($cookie->getPath()) > strlen($longest_path)) {
$value = $cookie->getValue();
$longest_path = $cookie->getPath();
}
}
}
return (isset($value) ? $value : false);
}
/**
* Tests cookie for matching against search
* criteria.
* @param SimpleTest $cookie Cookie to test.
* @param string $host Host must match.
* @param string $path Cookie path must be shorter than
* this path.
* @param string $name Name must match.
* @return boolean True if matched.
* @access private
*/
function _isMatch($cookie, $host, $path, $name) {
if ($cookie->getName() != $name) {
return false;
}
if ($host && $cookie->getHost() && ! $cookie->isValidHost($host)) {
return false;
}
if (! $cookie->isValidPath($path)) {
return false;
}
return true;
}
/**
* Uses a URL to sift relevant cookies by host and
* path. Results are list of strings of form "name=value".
* @param SimpleUrl $url Url to select by.
* @return array Valid name and value pairs.
* @access public
*/
function selectAsPairs($url) {
$pairs = array();
foreach ($this->_cookies as $cookie) {
if ($this->_isMatch($cookie, $url->getHost(), $url->getPath(), $cookie->getName())) {
$pairs[] = $cookie->getName() . '=' . $cookie->getValue();
}
}
return $pairs;
}
}
?>

552
thirdparty/simpletest/encoding.php vendored Normal file
View File

@ -0,0 +1,552 @@
<?php
/**
* base include file for SimpleTest
* @package SimpleTest
* @subpackage WebTester
* @version $Id: encoding.php 1723 2008-04-08 00:34:10Z lastcraft $
*/
/**#@+
* include other SimpleTest class files
*/
require_once(dirname(__FILE__) . '/socket.php');
/**#@-*/
/**
* Single post parameter.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleEncodedPair {
var $_key;
var $_value;
/**
* Stashes the data for rendering later.
* @param string $key Form element name.
* @param string $value Data to send.
*/
function SimpleEncodedPair($key, $value) {
$this->_key = $key;
$this->_value = $value;
}
/**
* The pair as a single string.
* @return string Encoded pair.
* @access public
*/
function asRequest() {
return urlencode($this->_key) . '=' . urlencode($this->_value);
}
/**
* The MIME part as a string.
* @return string MIME part encoding.
* @access public
*/
function asMime() {
$part = 'Content-Disposition: form-data; ';
$part .= "name=\"" . $this->_key . "\"\r\n";
$part .= "\r\n" . $this->_value;
return $part;
}
/**
* Is this the value we are looking for?
* @param string $key Identifier.
* @return boolean True if matched.
* @access public
*/
function isKey($key) {
return $key == $this->_key;
}
/**
* Is this the value we are looking for?
* @return string Identifier.
* @access public
*/
function getKey() {
return $this->_key;
}
/**
* Is this the value we are looking for?
* @return string Content.
* @access public
*/
function getValue() {
return $this->_value;
}
}
/**
* Single post parameter.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleAttachment {
var $_key;
var $_content;
var $_filename;
/**
* Stashes the data for rendering later.
* @param string $key Key to add value to.
* @param string $content Raw data.
* @param hash $filename Original filename.
*/
function SimpleAttachment($key, $content, $filename) {
$this->_key = $key;
$this->_content = $content;
$this->_filename = $filename;
}
/**
* The pair as a single string.
* @return string Encoded pair.
* @access public
*/
function asRequest() {
return '';
}
/**
* The MIME part as a string.
* @return string MIME part encoding.
* @access public
*/
function asMime() {
$part = 'Content-Disposition: form-data; ';
$part .= 'name="' . $this->_key . '"; ';
$part .= 'filename="' . $this->_filename . '"';
$part .= "\r\nContent-Type: " . $this->_deduceMimeType();
$part .= "\r\n\r\n" . $this->_content;
return $part;
}
/**
* Attempts to figure out the MIME type from the
* file extension and the content.
* @return string MIME type.
* @access private
*/
function _deduceMimeType() {
if ($this->_isOnlyAscii($this->_content)) {
return 'text/plain';
}
return 'application/octet-stream';
}
/**
* Tests each character is in the range 0-127.
* @param string $ascii String to test.
* @access private
*/
function _isOnlyAscii($ascii) {
for ($i = 0, $length = strlen($ascii); $i < $length; $i++) {
if (ord($ascii[$i]) > 127) {
return false;
}
}
return true;
}
/**
* Is this the value we are looking for?
* @param string $key Identifier.
* @return boolean True if matched.
* @access public
*/
function isKey($key) {
return $key == $this->_key;
}
/**
* Is this the value we are looking for?
* @return string Identifier.
* @access public
*/
function getKey() {
return $this->_key;
}
/**
* Is this the value we are looking for?
* @return string Content.
* @access public
*/
function getValue() {
return $this->_filename;
}
}
/**
* Bundle of GET/POST parameters. Can include
* repeated parameters.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleEncoding {
var $_request;
/**
* Starts empty.
* @param array $query Hash of parameters.
* Multiple values are
* as lists on a single key.
* @access public
*/
function SimpleEncoding($query = false) {
if (! $query) {
$query = array();
}
$this->clear();
$this->merge($query);
}
/**
* Empties the request of parameters.
* @access public
*/
function clear() {
$this->_request = array();
}
/**
* Adds a parameter to the query.
* @param string $key Key to add value to.
* @param string/array $value New data.
* @access public
*/
function add($key, $value) {
if ($value === false) {
return;
}
if (is_array($value)) {
foreach ($value as $item) {
$this->_addPair($key, $item);
}
} else {
$this->_addPair($key, $value);
}
}
/**
* Adds a new value into the request.
* @param string $key Key to add value to.
* @param string/array $value New data.
* @access private
*/
function _addPair($key, $value) {
$this->_request[] = new SimpleEncodedPair($key, $value);
}
/**
* Adds a MIME part to the query. Does nothing for a
* form encoded packet.
* @param string $key Key to add value to.
* @param string $content Raw data.
* @param hash $filename Original filename.
* @access public
*/
function attach($key, $content, $filename) {
$this->_request[] = new SimpleAttachment($key, $content, $filename);
}
/**
* Adds a set of parameters to this query.
* @param array/SimpleQueryString $query Multiple values are
* as lists on a single key.
* @access public
*/
function merge($query) {
if (is_object($query)) {
$this->_request = array_merge($this->_request, $query->getAll());
} elseif (is_array($query)) {
foreach ($query as $key => $value) {
$this->add($key, $value);
}
}
}
/**
* Accessor for single value.
* @return string/array False if missing, string
* if present and array if
* multiple entries.
* @access public
*/
function getValue($key) {
$values = array();
foreach ($this->_request as $pair) {
if ($pair->isKey($key)) {
$values[] = $pair->getValue();
}
}
if (count($values) == 0) {
return false;
} elseif (count($values) == 1) {
return $values[0];
} else {
return $values;
}
}
/**
* Accessor for listing of pairs.
* @return array All pair objects.
* @access public
*/
function getAll() {
return $this->_request;
}
/**
* Renders the query string as a URL encoded
* request part.
* @return string Part of URL.
* @access protected
*/
function _encode() {
$statements = array();
foreach ($this->_request as $pair) {
if ($statement = $pair->asRequest()) {
$statements[] = $statement;
}
}
return implode('&', $statements);
}
}
/**
* Bundle of GET parameters. Can include
* repeated parameters.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleGetEncoding extends SimpleEncoding {
/**
* Starts empty.
* @param array $query Hash of parameters.
* Multiple values are
* as lists on a single key.
* @access public
*/
function SimpleGetEncoding($query = false) {
$this->SimpleEncoding($query);
}
/**
* HTTP request method.
* @return string Always GET.
* @access public
*/
function getMethod() {
return 'GET';
}
/**
* Writes no extra headers.
* @param SimpleSocket $socket Socket to write to.
* @access public
*/
function writeHeadersTo(&$socket) {
}
/**
* No data is sent to the socket as the data is encoded into
* the URL.
* @param SimpleSocket $socket Socket to write to.
* @access public
*/
function writeTo(&$socket) {
}
/**
* Renders the query string as a URL encoded
* request part for attaching to a URL.
* @return string Part of URL.
* @access public
*/
function asUrlRequest() {
return $this->_encode();
}
}
/**
* Bundle of URL parameters for a HEAD request.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleHeadEncoding extends SimpleGetEncoding {
/**
* Starts empty.
* @param array $query Hash of parameters.
* Multiple values are
* as lists on a single key.
* @access public
*/
function SimpleHeadEncoding($query = false) {
$this->SimpleGetEncoding($query);
}
/**
* HTTP request method.
* @return string Always HEAD.
* @access public
*/
function getMethod() {
return 'HEAD';
}
}
/**
* Bundle of POST parameters. Can include
* repeated parameters.
* @package SimpleTest
* @subpackage WebTester
*/
class SimplePostEncoding extends SimpleEncoding {
/**
* Starts empty.
* @param array $query Hash of parameters.
* Multiple values are
* as lists on a single key.
* @access public
*/
function SimplePostEncoding($query = false) {
if (is_array($query) and $this->hasMoreThanOneLevel($query)) {
$query = $this->rewriteArrayWithMultipleLevels($query);
}
$this->SimpleEncoding($query);
}
function hasMoreThanOneLevel($query) {
foreach ($query as $key => $value) {
if (is_array($value)) {
return true;
}
}
return false;
}
function rewriteArrayWithMultipleLevels($query) {
$query_ = array();
foreach ($query as $key => $value) {
if (is_array($value)) {
foreach ($value as $sub_key => $sub_value) {
$query_[$key."[".$sub_key."]"] = $sub_value;
}
} else {
$query_[$key] = $value;
}
}
if ($this->hasMoreThanOneLevel($query_)) {
$query_ = $this->rewriteArrayWithMultipleLevels($query_);
}
return $query_;
}
/**
* HTTP request method.
* @return string Always POST.
* @access public
*/
function getMethod() {
return 'POST';
}
/**
* Dispatches the form headers down the socket.
* @param SimpleSocket $socket Socket to write to.
* @access public
*/
function writeHeadersTo(&$socket) {
$socket->write("Content-Length: " . (integer)strlen($this->_encode()) . "\r\n");
$socket->write("Content-Type: application/x-www-form-urlencoded\r\n");
}
/**
* Dispatches the form data down the socket.
* @param SimpleSocket $socket Socket to write to.
* @access public
*/
function writeTo(&$socket) {
$socket->write($this->_encode());
}
/**
* Renders the query string as a URL encoded
* request part for attaching to a URL.
* @return string Part of URL.
* @access public
*/
function asUrlRequest() {
return '';
}
}
/**
* Bundle of POST parameters in the multipart
* format. Can include file uploads.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleMultipartEncoding extends SimplePostEncoding {
var $_boundary;
/**
* Starts empty.
* @param array $query Hash of parameters.
* Multiple values are
* as lists on a single key.
* @access public
*/
function SimpleMultipartEncoding($query = false, $boundary = false) {
$this->SimplePostEncoding($query);
$this->_boundary = ($boundary === false ? uniqid('st') : $boundary);
}
/**
* Dispatches the form headers down the socket.
* @param SimpleSocket $socket Socket to write to.
* @access public
*/
function writeHeadersTo(&$socket) {
$socket->write("Content-Length: " . (integer)strlen($this->_encode()) . "\r\n");
$socket->write("Content-Type: multipart/form-data, boundary=" . $this->_boundary . "\r\n");
}
/**
* Dispatches the form data down the socket.
* @param SimpleSocket $socket Socket to write to.
* @access public
*/
function writeTo(&$socket) {
$socket->write($this->_encode());
}
/**
* Renders the query string as a URL encoded
* request part.
* @return string Part of URL.
* @access public
*/
function _encode() {
$stream = '';
foreach ($this->_request as $pair) {
$stream .= "--" . $this->_boundary . "\r\n";
$stream .= $pair->asMime() . "\r\n";
}
$stream .= "--" . $this->_boundary . "--\r\n";
return $stream;
}
}
?>

355
thirdparty/simpletest/form.php vendored Normal file
View File

@ -0,0 +1,355 @@
<?php
/**
* Base include file for SimpleTest.
* @package SimpleTest
* @subpackage WebTester
* @version $Id: form.php 1672 2008-03-02 04:47:34Z edwardzyang $
*/
/**#@+
* include SimpleTest files
*/
require_once(dirname(__FILE__) . '/tag.php');
require_once(dirname(__FILE__) . '/encoding.php');
require_once(dirname(__FILE__) . '/selector.php');
/**#@-*/
/**
* Form tag class to hold widget values.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleForm {
var $_method;
var $_action;
var $_encoding;
var $_default_target;
var $_id;
var $_buttons;
var $_images;
var $_widgets;
var $_radios;
var $_checkboxes;
/**
* Starts with no held controls/widgets.
* @param SimpleTag $tag Form tag to read.
* @param SimplePage $page Holding page.
*/
function SimpleForm($tag, &$page) {
$this->_method = $tag->getAttribute('method');
$this->_action = $this->_createAction($tag->getAttribute('action'), $page);
$this->_encoding = $this->_setEncodingClass($tag);
$this->_default_target = false;
$this->_id = $tag->getAttribute('id');
$this->_buttons = array();
$this->_images = array();
$this->_widgets = array();
$this->_radios = array();
$this->_checkboxes = array();
}
/**
* Creates the request packet to be sent by the form.
* @param SimpleTag $tag Form tag to read.
* @return string Packet class.
* @access private
*/
function _setEncodingClass($tag) {
if (strtolower($tag->getAttribute('method')) == 'post') {
if (strtolower($tag->getAttribute('enctype')) == 'multipart/form-data') {
return 'SimpleMultipartEncoding';
}
return 'SimplePostEncoding';
}
return 'SimpleGetEncoding';
}
/**
* Sets the frame target within a frameset.
* @param string $frame Name of frame.
* @access public
*/
function setDefaultTarget($frame) {
$this->_default_target = $frame;
}
/**
* Accessor for method of form submission.
* @return string Either get or post.
* @access public
*/
function getMethod() {
return ($this->_method ? strtolower($this->_method) : 'get');
}
/**
* Combined action attribute with current location
* to get an absolute form target.
* @param string $action Action attribute from form tag.
* @param SimpleUrl $base Page location.
* @return SimpleUrl Absolute form target.
*/
function _createAction($action, &$page) {
if (($action === '') || ($action === false)) {
return $page->expandUrl($page->getUrl());
}
return $page->expandUrl(new SimpleUrl($action));;
}
/**
* Absolute URL of the target.
* @return SimpleUrl URL target.
* @access public
*/
function getAction() {
$url = $this->_action;
if ($this->_default_target && ! $url->getTarget()) {
$url->setTarget($this->_default_target);
}
return $url;
}
/**
* Creates the encoding for the current values in the
* form.
* @return SimpleFormEncoding Request to submit.
* @access private
*/
function _encode() {
$class = $this->_encoding;
$encoding = new $class();
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
$this->_widgets[$i]->write($encoding);
}
return $encoding;
}
/**
* ID field of form for unique identification.
* @return string Unique tag ID.
* @access public
*/
function getId() {
return $this->_id;
}
/**
* Adds a tag contents to the form.
* @param SimpleWidget $tag Input tag to add.
* @access public
*/
function addWidget(&$tag) {
if (strtolower($tag->getAttribute('type')) == 'submit') {
$this->_buttons[] = &$tag;
} elseif (strtolower($tag->getAttribute('type')) == 'image') {
$this->_images[] = &$tag;
} elseif ($tag->getName()) {
$this->_setWidget($tag);
}
}
/**
* Sets the widget into the form, grouping radio
* buttons if any.
* @param SimpleWidget $tag Incoming form control.
* @access private
*/
function _setWidget(&$tag) {
if (strtolower($tag->getAttribute('type')) == 'radio') {
$this->_addRadioButton($tag);
} elseif (strtolower($tag->getAttribute('type')) == 'checkbox') {
$this->_addCheckbox($tag);
} else {
$this->_widgets[] = &$tag;
}
}
/**
* Adds a radio button, building a group if necessary.
* @param SimpleRadioButtonTag $tag Incoming form control.
* @access private
*/
function _addRadioButton(&$tag) {
if (! isset($this->_radios[$tag->getName()])) {
$this->_widgets[] = &new SimpleRadioGroup();
$this->_radios[$tag->getName()] = count($this->_widgets) - 1;
}
$this->_widgets[$this->_radios[$tag->getName()]]->addWidget($tag);
}
/**
* Adds a checkbox, making it a group on a repeated name.
* @param SimpleCheckboxTag $tag Incoming form control.
* @access private
*/
function _addCheckbox(&$tag) {
if (! isset($this->_checkboxes[$tag->getName()])) {
$this->_widgets[] = &$tag;
$this->_checkboxes[$tag->getName()] = count($this->_widgets) - 1;
} else {
$index = $this->_checkboxes[$tag->getName()];
if (! SimpleTestCompatibility::isA($this->_widgets[$index], 'SimpleCheckboxGroup')) {
$previous = &$this->_widgets[$index];
$this->_widgets[$index] = &new SimpleCheckboxGroup();
$this->_widgets[$index]->addWidget($previous);
}
$this->_widgets[$index]->addWidget($tag);
}
}
/**
* Extracts current value from form.
* @param SimpleSelector $selector Criteria to apply.
* @return string/array Value(s) as string or null
* if not set.
* @access public
*/
function getValue($selector) {
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
if ($selector->isMatch($this->_widgets[$i])) {
return $this->_widgets[$i]->getValue();
}
}
foreach ($this->_buttons as $button) {
if ($selector->isMatch($button)) {
return $button->getValue();
}
}
return null;
}
/**
* Sets a widget value within the form.
* @param SimpleSelector $selector Criteria to apply.
* @param string $value Value to input into the widget.
* @return boolean True if value is legal, false
* otherwise. If the field is not
* present, nothing will be set.
* @access public
*/
function setField($selector, $value, $position=false) {
$success = false;
$_position = 0;
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
if ($selector->isMatch($this->_widgets[$i])) {
$_position++;
if ($position === false or $_position === (int)$position) {
if ($this->_widgets[$i]->setValue($value)) {
$success = true;
}
}
}
}
return $success;
}
/**
* Used by the page object to set widgets labels to
* external label tags.
* @param SimpleSelector $selector Criteria to apply.
* @access public
*/
function attachLabelBySelector($selector, $label) {
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
if ($selector->isMatch($this->_widgets[$i])) {
if (method_exists($this->_widgets[$i], 'setLabel')) {
$this->_widgets[$i]->setLabel($label);
return;
}
}
}
}
/**
* Test to see if a form has a submit button.
* @param SimpleSelector $selector Criteria to apply.
* @return boolean True if present.
* @access public
*/
function hasSubmit($selector) {
foreach ($this->_buttons as $button) {
if ($selector->isMatch($button)) {
return true;
}
}
return false;
}
/**
* Test to see if a form has an image control.
* @param SimpleSelector $selector Criteria to apply.
* @return boolean True if present.
* @access public
*/
function hasImage($selector) {
foreach ($this->_images as $image) {
if ($selector->isMatch($image)) {
return true;
}
}
return false;
}
/**
* Gets the submit values for a selected button.
* @param SimpleSelector $selector Criteria to apply.
* @param hash $additional Additional data for the form.
* @return SimpleEncoding Submitted values or false
* if there is no such button
* in the form.
* @access public
*/
function submitButton($selector, $additional = false) {
$additional = $additional ? $additional : array();
foreach ($this->_buttons as $button) {
if ($selector->isMatch($button)) {
$encoding = $this->_encode();
$button->write($encoding);
if ($additional) {
$encoding->merge($additional);
}
return $encoding;
}
}
return false;
}
/**
* Gets the submit values for an image.
* @param SimpleSelector $selector Criteria to apply.
* @param integer $x X-coordinate of click.
* @param integer $y Y-coordinate of click.
* @param hash $additional Additional data for the form.
* @return SimpleEncoding Submitted values or false
* if there is no such button in the
* form.
* @access public
*/
function submitImage($selector, $x, $y, $additional = false) {
$additional = $additional ? $additional : array();
foreach ($this->_images as $image) {
if ($selector->isMatch($image)) {
$encoding = $this->_encode();
$image->write($encoding, $x, $y);
if ($additional) {
$encoding->merge($additional);
}
return $encoding;
}
}
return false;
}
/**
* Simply submits the form without the submit button
* value. Used when there is only one button or it
* is unimportant.
* @return hash Submitted values.
* @access public
*/
function submit() {
return $this->_encode();
}
}
?>

624
thirdparty/simpletest/http.php vendored Normal file
View File

@ -0,0 +1,624 @@
<?php
/**
* base include file for SimpleTest
* @package SimpleTest
* @subpackage WebTester
* @version $Id: http.php 1722 2008-04-07 19:30:56Z lastcraft $
*/
/**#@+
* include other SimpleTest class files
*/
require_once(dirname(__FILE__) . '/socket.php');
require_once(dirname(__FILE__) . '/cookies.php');
require_once(dirname(__FILE__) . '/url.php');
/**#@-*/
/**
* Creates HTTP headers for the end point of
* a HTTP request.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleRoute {
var $_url;
/**
* Sets the target URL.
* @param SimpleUrl $url URL as object.
* @access public
*/
function SimpleRoute($url) {
$this->_url = $url;
}
/**
* Resource name.
* @return SimpleUrl Current url.
* @access protected
*/
function getUrl() {
return $this->_url;
}
/**
* Creates the first line which is the actual request.
* @param string $method HTTP request method, usually GET.
* @return string Request line content.
* @access protected
*/
function _getRequestLine($method) {
return $method . ' ' . $this->_url->getPath() .
$this->_url->getEncodedRequest() . ' HTTP/1.0';
}
/**
* Creates the host part of the request.
* @return string Host line content.
* @access protected
*/
function _getHostLine() {
$line = 'Host: ' . $this->_url->getHost();
if ($this->_url->getPort()) {
$line .= ':' . $this->_url->getPort();
}
return $line;
}
/**
* Opens a socket to the route.
* @param string $method HTTP request method, usually GET.
* @param integer $timeout Connection timeout.
* @return SimpleSocket New socket.
* @access public
*/
function &createConnection($method, $timeout) {
$default_port = ('https' == $this->_url->getScheme()) ? 443 : 80;
$socket = &$this->_createSocket(
$this->_url->getScheme() ? $this->_url->getScheme() : 'http',
$this->_url->getHost(),
$this->_url->getPort() ? $this->_url->getPort() : $default_port,
$timeout);
if (! $socket->isError()) {
$socket->write($this->_getRequestLine($method) . "\r\n");
$socket->write($this->_getHostLine() . "\r\n");
$socket->write("Connection: close\r\n");
}
return $socket;
}
/**
* Factory for socket.
* @param string $scheme Protocol to use.
* @param string $host Hostname to connect to.
* @param integer $port Remote port.
* @param integer $timeout Connection timeout.
* @return SimpleSocket/SimpleSecureSocket New socket.
* @access protected
*/
function &_createSocket($scheme, $host, $port, $timeout) {
if (in_array($scheme, array('https'))) {
$socket = &new SimpleSecureSocket($host, $port, $timeout);
} else {
$socket = &new SimpleSocket($host, $port, $timeout);
}
return $socket;
}
}
/**
* Creates HTTP headers for the end point of
* a HTTP request via a proxy server.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleProxyRoute extends SimpleRoute {
var $_proxy;
var $_username;
var $_password;
/**
* Stashes the proxy address.
* @param SimpleUrl $url URL as object.
* @param string $proxy Proxy URL.
* @param string $username Username for autentication.
* @param string $password Password for autentication.
* @access public
*/
function SimpleProxyRoute($url, $proxy, $username = false, $password = false) {
$this->SimpleRoute($url);
$this->_proxy = $proxy;
$this->_username = $username;
$this->_password = $password;
}
/**
* Creates the first line which is the actual request.
* @param string $method HTTP request method, usually GET.
* @param SimpleUrl $url URL as object.
* @return string Request line content.
* @access protected
*/
function _getRequestLine($method) {
$url = $this->getUrl();
$scheme = $url->getScheme() ? $url->getScheme() : 'http';
$port = $url->getPort() ? ':' . $url->getPort() : '';
return $method . ' ' . $scheme . '://' . $url->getHost() . $port .
$url->getPath() . $url->getEncodedRequest() . ' HTTP/1.0';
}
/**
* Creates the host part of the request.
* @param SimpleUrl $url URL as object.
* @return string Host line content.
* @access protected
*/
function _getHostLine() {
$host = 'Host: ' . $this->_proxy->getHost();
$port = $this->_proxy->getPort() ? $this->_proxy->getPort() : 8080;
return "$host:$port";
}
/**
* Opens a socket to the route.
* @param string $method HTTP request method, usually GET.
* @param integer $timeout Connection timeout.
* @return SimpleSocket New socket.
* @access public
*/
function &createConnection($method, $timeout) {
$socket = &$this->_createSocket(
$this->_proxy->getScheme() ? $this->_proxy->getScheme() : 'http',
$this->_proxy->getHost(),
$this->_proxy->getPort() ? $this->_proxy->getPort() : 8080,
$timeout);
if ($socket->isError()) {
return $socket;
}
$socket->write($this->_getRequestLine($method) . "\r\n");
$socket->write($this->_getHostLine() . "\r\n");
if ($this->_username && $this->_password) {
$socket->write('Proxy-Authorization: Basic ' .
base64_encode($this->_username . ':' . $this->_password) .
"\r\n");
}
$socket->write("Connection: close\r\n");
return $socket;
}
}
/**
* HTTP request for a web page. Factory for
* HttpResponse object.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleHttpRequest {
var $_route;
var $_encoding;
var $_headers;
var $_cookies;
/**
* Builds the socket request from the different pieces.
* These include proxy information, URL, cookies, headers,
* request method and choice of encoding.
* @param SimpleRoute $route Request route.
* @param SimpleFormEncoding $encoding Content to send with
* request.
* @access public
*/
function SimpleHttpRequest(&$route, $encoding) {
$this->_route = &$route;
$this->_encoding = $encoding;
$this->_headers = array();
$this->_cookies = array();
}
/**
* Dispatches the content to the route's socket.
* @param integer $timeout Connection timeout.
* @return SimpleHttpResponse A response which may only have
* an error, but hopefully has a
* complete web page.
* @access public
*/
function &fetch($timeout) {
$socket = &$this->_route->createConnection($this->_encoding->getMethod(), $timeout);
if (! $socket->isError()) {
$this->_dispatchRequest($socket, $this->_encoding);
}
$response = &$this->_createResponse($socket);
return $response;
}
/**
* Sends the headers.
* @param SimpleSocket $socket Open socket.
* @param string $method HTTP request method,
* usually GET.
* @param SimpleFormEncoding $encoding Content to send with request.
* @access private
*/
function _dispatchRequest(&$socket, $encoding) {
foreach ($this->_headers as $header_line) {
$socket->write($header_line . "\r\n");
}
if (count($this->_cookies) > 0) {
$socket->write("Cookie: " . implode(";", $this->_cookies) . "\r\n");
}
$encoding->writeHeadersTo($socket);
$socket->write("\r\n");
$encoding->writeTo($socket);
}
/**
* Adds a header line to the request.
* @param string $header_line Text of full header line.
* @access public
*/
function addHeaderLine($header_line) {
$this->_headers[] = $header_line;
}
/**
* Reads all the relevant cookies from the
* cookie jar.
* @param SimpleCookieJar $jar Jar to read
* @param SimpleUrl $url Url to use for scope.
* @access public
*/
function readCookiesFromJar($jar, $url) {
$this->_cookies = $jar->selectAsPairs($url);
}
/**
* Wraps the socket in a response parser.
* @param SimpleSocket $socket Responding socket.
* @return SimpleHttpResponse Parsed response object.
* @access protected
*/
function &_createResponse(&$socket) {
$response = &new SimpleHttpResponse(
$socket,
$this->_route->getUrl(),
$this->_encoding);
return $response;
}
}
/**
* Collection of header lines in the response.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleHttpHeaders {
var $_raw_headers;
var $_response_code;
var $_http_version;
var $_mime_type;
var $_location;
var $_cookies;
var $_authentication;
var $_realm;
/**
* Parses the incoming header block.
* @param string $headers Header block.
* @access public
*/
function SimpleHttpHeaders($headers) {
$this->_raw_headers = $headers;
$this->_response_code = false;
$this->_http_version = false;
$this->_mime_type = '';
$this->_location = false;
$this->_cookies = array();
$this->_authentication = false;
$this->_realm = false;
foreach (split("\r\n", $headers) as $header_line) {
$this->_parseHeaderLine($header_line);
}
}
/**
* Accessor for parsed HTTP protocol version.
* @return integer HTTP error code.
* @access public
*/
function getHttpVersion() {
return $this->_http_version;
}
/**
* Accessor for raw header block.
* @return string All headers as raw string.
* @access public
*/
function getRaw() {
return $this->_raw_headers;
}
/**
* Accessor for parsed HTTP error code.
* @return integer HTTP error code.
* @access public
*/
function getResponseCode() {
return (integer)$this->_response_code;
}
/**
* Returns the redirected URL or false if
* no redirection.
* @return string URL or false for none.
* @access public
*/
function getLocation() {
return $this->_location;
}
/**
* Test to see if the response is a valid redirect.
* @return boolean True if valid redirect.
* @access public
*/
function isRedirect() {
return in_array($this->_response_code, array(301, 302, 303, 307)) &&
(boolean)$this->getLocation();
}
/**
* Test to see if the response is an authentication
* challenge.
* @return boolean True if challenge.
* @access public
*/
function isChallenge() {
return ($this->_response_code == 401) &&
(boolean)$this->_authentication &&
(boolean)$this->_realm;
}
/**
* Accessor for MIME type header information.
* @return string MIME type.
* @access public
*/
function getMimeType() {
return $this->_mime_type;
}
/**
* Accessor for authentication type.
* @return string Type.
* @access public
*/
function getAuthentication() {
return $this->_authentication;
}
/**
* Accessor for security realm.
* @return string Realm.
* @access public
*/
function getRealm() {
return $this->_realm;
}
/**
* Writes new cookies to the cookie jar.
* @param SimpleCookieJar $jar Jar to write to.
* @param SimpleUrl $url Host and path to write under.
* @access public
*/
function writeCookiesToJar(&$jar, $url) {
foreach ($this->_cookies as $cookie) {
$jar->setCookie(
$cookie->getName(),
$cookie->getValue(),
$url->getHost(),
$cookie->getPath(),
$cookie->getExpiry());
}
}
/**
* Called on each header line to accumulate the held
* data within the class.
* @param string $header_line One line of header.
* @access protected
*/
function _parseHeaderLine($header_line) {
if (preg_match('/HTTP\/(\d+\.\d+)\s+(\d+)/i', $header_line, $matches)) {
$this->_http_version = $matches[1];
$this->_response_code = $matches[2];
}
if (preg_match('/Content-type:\s*(.*)/i', $header_line, $matches)) {
$this->_mime_type = trim($matches[1]);
}
if (preg_match('/Location:\s*(.*)/i', $header_line, $matches)) {
$this->_location = trim($matches[1]);
}
if (preg_match('/Set-cookie:(.*)/i', $header_line, $matches)) {
$this->_cookies[] = $this->_parseCookie($matches[1]);
}
if (preg_match('/WWW-Authenticate:\s+(\S+)\s+realm=\"(.*?)\"/i', $header_line, $matches)) {
$this->_authentication = $matches[1];
$this->_realm = trim($matches[2]);
}
}
/**
* Parse the Set-cookie content.
* @param string $cookie_line Text after "Set-cookie:"
* @return SimpleCookie New cookie object.
* @access private
*/
function _parseCookie($cookie_line) {
$parts = split(";", $cookie_line);
$cookie = array();
preg_match('/\s*(.*?)\s*=(.*)/', array_shift($parts), $cookie);
foreach ($parts as $part) {
if (preg_match('/\s*(.*?)\s*=(.*)/', $part, $matches)) {
$cookie[$matches[1]] = trim($matches[2]);
}
}
return new SimpleCookie(
$cookie[1],
trim($cookie[2]),
isset($cookie["path"]) ? $cookie["path"] : "",
isset($cookie["expires"]) ? $cookie["expires"] : false);
}
}
/**
* Basic HTTP response.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleHttpResponse extends SimpleStickyError {
var $_url;
var $_encoding;
var $_sent;
var $_content;
var $_headers;
/**
* Constructor. Reads and parses the incoming
* content and headers.
* @param SimpleSocket $socket Network connection to fetch
* response text from.
* @param SimpleUrl $url Resource name.
* @param mixed $encoding Record of content sent.
* @access public
*/
function SimpleHttpResponse(&$socket, $url, $encoding) {
$this->SimpleStickyError();
$this->_url = $url;
$this->_encoding = $encoding;
$this->_sent = $socket->getSent();
$this->_content = false;
$raw = $this->_readAll($socket);
if ($socket->isError()) {
$this->_setError('Error reading socket [' . $socket->getError() . ']');
return;
}
$this->_parse($raw);
}
/**
* Splits up the headers and the rest of the content.
* @param string $raw Content to parse.
* @access private
*/
function _parse($raw) {
if (! $raw) {
$this->_setError('Nothing fetched');
$this->_headers = &new SimpleHttpHeaders('');
} elseif (! strstr($raw, "\r\n\r\n")) {
$this->_setError('Could not split headers from content');
$this->_headers = &new SimpleHttpHeaders($raw);
} else {
list($headers, $this->_content) = split("\r\n\r\n", $raw, 2);
$this->_headers = &new SimpleHttpHeaders($headers);
}
}
/**
* Original request method.
* @return string GET, POST or HEAD.
* @access public
*/
function getMethod() {
return $this->_encoding->getMethod();
}
/**
* Resource name.
* @return SimpleUrl Current url.
* @access public
*/
function getUrl() {
return $this->_url;
}
/**
* Original request data.
* @return mixed Sent content.
* @access public
*/
function getRequestData() {
return $this->_encoding;
}
/**
* Raw request that was sent down the wire.
* @return string Bytes actually sent.
* @access public
*/
function getSent() {
return $this->_sent;
}
/**
* Accessor for the content after the last
* header line.
* @return string All content.
* @access public
*/
function getContent() {
return $this->_content;
}
/**
* Accessor for header block. The response is the
* combination of this and the content.
* @return SimpleHeaders Wrapped header block.
* @access public
*/
function getHeaders() {
return $this->_headers;
}
/**
* Accessor for any new cookies.
* @return array List of new cookies.
* @access public
*/
function getNewCookies() {
return $this->_headers->getNewCookies();
}
/**
* Reads the whole of the socket output into a
* single string.
* @param SimpleSocket $socket Unread socket.
* @return string Raw output if successful
* else false.
* @access private
*/
function _readAll(&$socket) {
$all = '';
while (! $this->_isLastPacket($next = $socket->read())) {
$all .= $next;
}
return $all;
}
/**
* Test to see if the packet from the socket is the
* last one.
* @param string $packet Chunk to interpret.
* @return boolean True if empty or EOF.
* @access private
*/
function _isLastPacket($packet) {
if (is_string($packet)) {
return $packet === '';
}
return ! $packet;
}
}
?>

983
thirdparty/simpletest/page.php vendored Normal file
View File

@ -0,0 +1,983 @@
<?php
/**
* Base include file for SimpleTest
* @package SimpleTest
* @subpackage WebTester
* @version $Id: page.php 1672 2008-03-02 04:47:34Z edwardzyang $
*/
/**#@+
* include other SimpleTest class files
*/
require_once(dirname(__FILE__) . '/http.php');
require_once(dirname(__FILE__) . '/parser.php');
require_once(dirname(__FILE__) . '/tag.php');
require_once(dirname(__FILE__) . '/form.php');
require_once(dirname(__FILE__) . '/selector.php');
/**#@-*/
/**
* Creates tags and widgets given HTML tag
* attributes.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleTagBuilder {
/**
* Factory for the tag objects. Creates the
* appropriate tag object for the incoming tag name
* and attributes.
* @param string $name HTML tag name.
* @param hash $attributes Element attributes.
* @return SimpleTag Tag object.
* @access public
*/
function createTag($name, $attributes) {
static $map = array(
'a' => 'SimpleAnchorTag',
'title' => 'SimpleTitleTag',
'base' => 'SimpleBaseTag',
'button' => 'SimpleButtonTag',
'textarea' => 'SimpleTextAreaTag',
'option' => 'SimpleOptionTag',
'label' => 'SimpleLabelTag',
'form' => 'SimpleFormTag',
'frame' => 'SimpleFrameTag');
$attributes = $this->_keysToLowerCase($attributes);
if (array_key_exists($name, $map)) {
$tag_class = $map[$name];
return new $tag_class($attributes);
} elseif ($name == 'select') {
return $this->_createSelectionTag($attributes);
} elseif ($name == 'input') {
return $this->_createInputTag($attributes);
}
return new SimpleTag($name, $attributes);
}
/**
* Factory for selection fields.
* @param hash $attributes Element attributes.
* @return SimpleTag Tag object.
* @access protected
*/
function _createSelectionTag($attributes) {
if (isset($attributes['multiple'])) {
return new MultipleSelectionTag($attributes);
}
return new SimpleSelectionTag($attributes);
}
/**
* Factory for input tags.
* @param hash $attributes Element attributes.
* @return SimpleTag Tag object.
* @access protected
*/
function _createInputTag($attributes) {
if (! isset($attributes['type'])) {
return new SimpleTextTag($attributes);
}
$type = strtolower(trim($attributes['type']));
$map = array(
'submit' => 'SimpleSubmitTag',
'image' => 'SimpleImageSubmitTag',
'checkbox' => 'SimpleCheckboxTag',
'radio' => 'SimpleRadioButtonTag',
'text' => 'SimpleTextTag',
'hidden' => 'SimpleTextTag',
'password' => 'SimpleTextTag',
'file' => 'SimpleUploadTag');
if (array_key_exists($type, $map)) {
$tag_class = $map[$type];
return new $tag_class($attributes);
}
return false;
}
/**
* Make the keys lower case for case insensitive look-ups.
* @param hash $map Hash to convert.
* @return hash Unchanged values, but keys lower case.
* @access private
*/
function _keysToLowerCase($map) {
$lower = array();
foreach ($map as $key => $value) {
$lower[strtolower($key)] = $value;
}
return $lower;
}
}
/**
* SAX event handler. Maintains a list of
* open tags and dispatches them as they close.
* @package SimpleTest
* @subpackage WebTester
*/
class SimplePageBuilder extends SimpleSaxListener {
var $_tags;
var $_page;
var $_private_content_tag;
/**
* Sets the builder up empty.
* @access public
*/
function SimplePageBuilder() {
$this->SimpleSaxListener();
}
/**
* Frees up any references so as to allow the PHP garbage
* collection from unset() to work.
* @access public
*/
function free() {
unset($this->_tags);
unset($this->_page);
unset($this->_private_content_tags);
}
/**
* Reads the raw content and send events
* into the page to be built.
* @param $response SimpleHttpResponse Fetched response.
* @return SimplePage Newly parsed page.
* @access public
*/
function &parse($response) {
$this->_tags = array();
$this->_page = &$this->_createPage($response);
$parser = &$this->_createParser($this);
$parser->parse($response->getContent());
$this->_page->acceptPageEnd();
return $this->_page;
}
/**
* Creates an empty page.
* @return SimplePage New unparsed page.
* @access protected
*/
function &_createPage($response) {
$page = &new SimplePage($response);
return $page;
}
/**
* Creates the parser used with the builder.
* @param $listener SimpleSaxListener Target of parser.
* @return SimpleSaxParser Parser to generate
* events for the builder.
* @access protected
*/
function &_createParser(&$listener) {
$parser = &new SimpleHtmlSaxParser($listener);
return $parser;
}
/**
* Start of element event. Opens a new tag.
* @param string $name Element name.
* @param hash $attributes Attributes without content
* are marked as true.
* @return boolean False on parse error.
* @access public
*/
function startElement($name, $attributes) {
$factory = &new SimpleTagBuilder();
$tag = $factory->createTag($name, $attributes);
if (! $tag) {
return true;
}
if ($tag->getTagName() == 'label') {
$this->_page->acceptLabelStart($tag);
$this->_openTag($tag);
return true;
}
if ($tag->getTagName() == 'form') {
$this->_page->acceptFormStart($tag);
return true;
}
if ($tag->getTagName() == 'frameset') {
$this->_page->acceptFramesetStart($tag);
return true;
}
if ($tag->getTagName() == 'frame') {
$this->_page->acceptFrame($tag);
return true;
}
if ($tag->isPrivateContent() && ! isset($this->_private_content_tag)) {
$this->_private_content_tag = &$tag;
}
if ($tag->expectEndTag()) {
$this->_openTag($tag);
return true;
}
$this->_page->acceptTag($tag);
return true;
}
/**
* End of element event.
* @param string $name Element name.
* @return boolean False on parse error.
* @access public
*/
function endElement($name) {
if ($name == 'label') {
$this->_page->acceptLabelEnd();
return true;
}
if ($name == 'form') {
$this->_page->acceptFormEnd();
return true;
}
if ($name == 'frameset') {
$this->_page->acceptFramesetEnd();
return true;
}
if ($this->_hasNamedTagOnOpenTagStack($name)) {
$tag = array_pop($this->_tags[$name]);
if ($tag->isPrivateContent() && $this->_private_content_tag->getTagName() == $name) {
unset($this->_private_content_tag);
}
$this->_addContentTagToOpenTags($tag);
$this->_page->acceptTag($tag);
return true;
}
return true;
}
/**
* Test to see if there are any open tags awaiting
* closure that match the tag name.
* @param string $name Element name.
* @return boolean True if any are still open.
* @access private
*/
function _hasNamedTagOnOpenTagStack($name) {
return isset($this->_tags[$name]) && (count($this->_tags[$name]) > 0);
}
/**
* Unparsed, but relevant data. The data is added
* to every open tag.
* @param string $text May include unparsed tags.
* @return boolean False on parse error.
* @access public
*/
function addContent($text) {
if (isset($this->_private_content_tag)) {
$this->_private_content_tag->addContent($text);
} else {
$this->_addContentToAllOpenTags($text);
}
return true;
}
/**
* Any content fills all currently open tags unless it
* is part of an option tag.
* @param string $text May include unparsed tags.
* @access private
*/
function _addContentToAllOpenTags($text) {
foreach (array_keys($this->_tags) as $name) {
for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) {
$this->_tags[$name][$i]->addContent($text);
}
}
}
/**
* Parsed data in tag form. The parsed tag is added
* to every open tag. Used for adding options to select
* fields only.
* @param SimpleTag $tag Option tags only.
* @access private
*/
function _addContentTagToOpenTags(&$tag) {
if ($tag->getTagName() != 'option') {
return;
}
foreach (array_keys($this->_tags) as $name) {
for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) {
$this->_tags[$name][$i]->addTag($tag);
}
}
}
/**
* Opens a tag for receiving content. Multiple tags
* will be receiving input at the same time.
* @param SimpleTag $tag New content tag.
* @access private
*/
function _openTag(&$tag) {
$name = $tag->getTagName();
if (! in_array($name, array_keys($this->_tags))) {
$this->_tags[$name] = array();
}
$this->_tags[$name][] = &$tag;
}
}
/**
* A wrapper for a web page.
* @package SimpleTest
* @subpackage WebTester
*/
class SimplePage {
var $_links;
var $_title;
var $_last_widget;
var $_label;
var $_left_over_labels;
var $_open_forms;
var $_complete_forms;
var $_frameset;
var $_frames;
var $_frameset_nesting_level;
var $_transport_error;
var $_raw;
var $_text;
var $_sent;
var $_headers;
var $_method;
var $_url;
var $_base = false;
var $_request_data;
/**
* Parses a page ready to access it's contents.
* @param SimpleHttpResponse $response Result of HTTP fetch.
* @access public
*/
function SimplePage($response = false) {
$this->_links = array();
$this->_title = false;
$this->_left_over_labels = array();
$this->_open_forms = array();
$this->_complete_forms = array();
$this->_frameset = false;
$this->_frames = array();
$this->_frameset_nesting_level = 0;
$this->_text = false;
if ($response) {
$this->_extractResponse($response);
} else {
$this->_noResponse();
}
}
/**
* Extracts all of the response information.
* @param SimpleHttpResponse $response Response being parsed.
* @access private
*/
function _extractResponse($response) {
$this->_transport_error = $response->getError();
$this->_raw = $response->getContent();
$this->_sent = $response->getSent();
$this->_headers = $response->getHeaders();
$this->_method = $response->getMethod();
$this->_url = $response->getUrl();
$this->_request_data = $response->getRequestData();
}
/**
* Sets up a missing response.
* @access private
*/
function _noResponse() {
$this->_transport_error = 'No page fetched yet';
$this->_raw = false;
$this->_sent = false;
$this->_headers = false;
$this->_method = 'GET';
$this->_url = false;
$this->_request_data = false;
}
/**
* Original request as bytes sent down the wire.
* @return mixed Sent content.
* @access public
*/
function getRequest() {
return $this->_sent;
}
/**
* Accessor for raw text of page.
* @return string Raw unparsed content.
* @access public
*/
function getRaw() {
return $this->_raw;
}
/**
* Accessor for plain text of page as a text browser
* would see it.
* @return string Plain text of page.
* @access public
*/
function getText() {
if (! $this->_text) {
$this->_text = SimpleHtmlSaxParser::normalise($this->_raw);
}
return $this->_text;
}
/**
* Accessor for raw headers of page.
* @return string Header block as text.
* @access public
*/
function getHeaders() {
if ($this->_headers) {
return $this->_headers->getRaw();
}
return false;
}
/**
* Original request method.
* @return string GET, POST or HEAD.
* @access public
*/
function getMethod() {
return $this->_method;
}
/**
* Original resource name.
* @return SimpleUrl Current url.
* @access public
*/
function getUrl() {
return $this->_url;
}
/**
* Base URL if set via BASE tag page url otherwise
* @return SimpleUrl Base url.
* @access public
*/
function getBaseUrl() {
return $this->_base;
}
/**
* Original request data.
* @return mixed Sent content.
* @access public
*/
function getRequestData() {
return $this->_request_data;
}
/**
* Accessor for last error.
* @return string Error from last response.
* @access public
*/
function getTransportError() {
return $this->_transport_error;
}
/**
* Accessor for current MIME type.
* @return string MIME type as string; e.g. 'text/html'
* @access public
*/
function getMimeType() {
if ($this->_headers) {
return $this->_headers->getMimeType();
}
return false;
}
/**
* Accessor for HTTP response code.
* @return integer HTTP response code received.
* @access public
*/
function getResponseCode() {
if ($this->_headers) {
return $this->_headers->getResponseCode();
}
return false;
}
/**
* Accessor for last Authentication type. Only valid
* straight after a challenge (401).
* @return string Description of challenge type.
* @access public
*/
function getAuthentication() {
if ($this->_headers) {
return $this->_headers->getAuthentication();
}
return false;
}
/**
* Accessor for last Authentication realm. Only valid
* straight after a challenge (401).
* @return string Name of security realm.
* @access public
*/
function getRealm() {
if ($this->_headers) {
return $this->_headers->getRealm();
}
return false;
}
/**
* Accessor for current frame focus. Will be
* false as no frames.
* @return array Always empty.
* @access public
*/
function getFrameFocus() {
return array();
}
/**
* Sets the focus by index. The integer index starts from 1.
* @param integer $choice Chosen frame.
* @return boolean Always false.
* @access public
*/
function setFrameFocusByIndex($choice) {
return false;
}
/**
* Sets the focus by name. Always fails for a leaf page.
* @param string $name Chosen frame.
* @return boolean False as no frames.
* @access public
*/
function setFrameFocus($name) {
return false;
}
/**
* Clears the frame focus. Does nothing for a leaf page.
* @access public
*/
function clearFrameFocus() {
}
/**
* Adds a tag to the page.
* @param SimpleTag $tag Tag to accept.
* @access public
*/
function acceptTag(&$tag) {
if ($tag->getTagName() == "a") {
$this->_addLink($tag);
} elseif ($tag->getTagName() == "base") {
$this->_setBase($tag);
} elseif ($tag->getTagName() == "title") {
$this->_setTitle($tag);
} elseif ($this->_isFormElement($tag->getTagName())) {
for ($i = 0; $i < count($this->_open_forms); $i++) {
$this->_open_forms[$i]->addWidget($tag);
}
$this->_last_widget = &$tag;
}
}
/**
* Opens a label for a described widget.
* @param SimpleFormTag $tag Tag to accept.
* @access public
*/
function acceptLabelStart(&$tag) {
$this->_label = &$tag;
unset($this->_last_widget);
}
/**
* Closes the most recently opened label.
* @access public
*/
function acceptLabelEnd() {
if (isset($this->_label)) {
if (isset($this->_last_widget)) {
$this->_last_widget->setLabel($this->_label->getText());
unset($this->_last_widget);
} else {
$this->_left_over_labels[] = SimpleTestCompatibility::copy($this->_label);
}
unset($this->_label);
}
}
/**
* Tests to see if a tag is a possible form
* element.
* @param string $name HTML element name.
* @return boolean True if form element.
* @access private
*/
function _isFormElement($name) {
return in_array($name, array('input', 'button', 'textarea', 'select'));
}
/**
* Opens a form. New widgets go here.
* @param SimpleFormTag $tag Tag to accept.
* @access public
*/
function acceptFormStart(&$tag) {
$this->_open_forms[] = &new SimpleForm($tag, $this);
}
/**
* Closes the most recently opened form.
* @access public
*/
function acceptFormEnd() {
if (count($this->_open_forms)) {
$this->_complete_forms[] = array_pop($this->_open_forms);
}
}
/**
* Opens a frameset. A frameset may contain nested
* frameset tags.
* @param SimpleFramesetTag $tag Tag to accept.
* @access public
*/
function acceptFramesetStart(&$tag) {
if (! $this->_isLoadingFrames()) {
$this->_frameset = &$tag;
}
$this->_frameset_nesting_level++;
}
/**
* Closes the most recently opened frameset.
* @access public
*/
function acceptFramesetEnd() {
if ($this->_isLoadingFrames()) {
$this->_frameset_nesting_level--;
}
}
/**
* Takes a single frame tag and stashes it in
* the current frame set.
* @param SimpleFrameTag $tag Tag to accept.
* @access public
*/
function acceptFrame(&$tag) {
if ($this->_isLoadingFrames()) {
if ($tag->getAttribute('src')) {
$this->_frames[] = &$tag;
}
}
}
/**
* Test to see if in the middle of reading
* a frameset.
* @return boolean True if inframeset.
* @access private
*/
function _isLoadingFrames() {
if (! $this->_frameset) {
return false;
}
return ($this->_frameset_nesting_level > 0);
}
/**
* Test to see if link is an absolute one.
* @param string $url Url to test.
* @return boolean True if absolute.
* @access protected
*/
function _linkIsAbsolute($url) {
$parsed = new SimpleUrl($url);
return (boolean)($parsed->getScheme() && $parsed->getHost());
}
/**
* Adds a link to the page.
* @param SimpleAnchorTag $tag Link to accept.
* @access protected
*/
function _addLink($tag) {
$this->_links[] = $tag;
}
/**
* Marker for end of complete page. Any work in
* progress can now be closed.
* @access public
*/
function acceptPageEnd() {
while (count($this->_open_forms)) {
$this->_complete_forms[] = array_pop($this->_open_forms);
}
foreach ($this->_left_over_labels as $label) {
for ($i = 0, $count = count($this->_complete_forms); $i < $count; $i++) {
$this->_complete_forms[$i]->attachLabelBySelector(
new SimpleById($label->getFor()),
$label->getText());
}
}
}
/**
* Test for the presence of a frameset.
* @return boolean True if frameset.
* @access public
*/
function hasFrames() {
return (boolean)$this->_frameset;
}
/**
* Accessor for frame name and source URL for every frame that
* will need to be loaded. Immediate children only.
* @return boolean/array False if no frameset or
* otherwise a hash of frame URLs.
* The key is either a numerical
* base one index or the name attribute.
* @access public
*/
function getFrameset() {
if (! $this->_frameset) {
return false;
}
$urls = array();
for ($i = 0; $i < count($this->_frames); $i++) {
$name = $this->_frames[$i]->getAttribute('name');
$url = new SimpleUrl($this->_frames[$i]->getAttribute('src'));
$urls[$name ? $name : $i + 1] = $this->expandUrl($url);
}
return $urls;
}
/**
* Fetches a list of loaded frames.
* @return array/string Just the URL for a single page.
* @access public
*/
function getFrames() {
$url = $this->expandUrl($this->getUrl());
return $url->asString();
}
/**
* Accessor for a list of all links.
* @return array List of urls with scheme of
* http or https and hostname.
* @access public
*/
function getUrls() {
$all = array();
foreach ($this->_links as $link) {
$url = $this->_getUrlFromLink($link);
$all[] = $url->asString();
}
return $all;
}
/**
* Accessor for URLs by the link label. Label will match
* regardess of whitespace issues and case.
* @param string $label Text of link.
* @return array List of links with that label.
* @access public
*/
function getUrlsByLabel($label) {
$matches = array();
foreach ($this->_links as $link) {
if ($link->getText() == $label) {
$matches[] = $this->_getUrlFromLink($link);
}
}
return $matches;
}
/**
* Accessor for a URL by the id attribute.
* @param string $id Id attribute of link.
* @return SimpleUrl URL with that id of false if none.
* @access public
*/
function getUrlById($id) {
foreach ($this->_links as $link) {
if ($link->getAttribute('id') === (string)$id) {
return $this->_getUrlFromLink($link);
}
}
return false;
}
/**
* Converts a link tag into a target URL.
* @param SimpleAnchor $link Parsed link.
* @return SimpleUrl URL with frame target if any.
* @access private
*/
function _getUrlFromLink($link) {
$url = $this->expandUrl($link->getHref());
if ($link->getAttribute('target')) {
$url->setTarget($link->getAttribute('target'));
}
return $url;
}
/**
* Expands expandomatic URLs into fully qualified
* URLs.
* @param SimpleUrl $url Relative URL.
* @return SimpleUrl Absolute URL.
* @access public
*/
function expandUrl($url) {
if (! is_object($url)) {
$url = new SimpleUrl($url);
}
$location = $this->getBaseUrl() ? $this->getBaseUrl() : new SimpleUrl();
return $url->makeAbsolute($location->makeAbsolute($this->getUrl()));
}
/**
* Sets the base url for the page.
* @param SimpleTag $tag Base URL for page.
* @access protected
*/
function _setBase(&$tag) {
$url = $tag->getAttribute('href');
$this->_base = new SimpleUrl($url);
}
/**
* Sets the title tag contents.
* @param SimpleTitleTag $tag Title of page.
* @access protected
*/
function _setTitle(&$tag) {
$this->_title = &$tag;
}
/**
* Accessor for parsed title.
* @return string Title or false if no title is present.
* @access public
*/
function getTitle() {
if ($this->_title) {
return $this->_title->getText();
}
return false;
}
/**
* Finds a held form by button label. Will only
* search correctly built forms.
* @param SimpleSelector $selector Button finder.
* @return SimpleForm Form object containing
* the button.
* @access public
*/
function &getFormBySubmit($selector) {
for ($i = 0; $i < count($this->_complete_forms); $i++) {
if ($this->_complete_forms[$i]->hasSubmit($selector)) {
return $this->_complete_forms[$i];
}
}
$null = null;
return $null;
}
/**
* Finds a held form by image using a selector.
* Will only search correctly built forms.
* @param SimpleSelector $selector Image finder.
* @return SimpleForm Form object containing
* the image.
* @access public
*/
function &getFormByImage($selector) {
for ($i = 0; $i < count($this->_complete_forms); $i++) {
if ($this->_complete_forms[$i]->hasImage($selector)) {
return $this->_complete_forms[$i];
}
}
$null = null;
return $null;
}
/**
* Finds a held form by the form ID. A way of
* identifying a specific form when we have control
* of the HTML code.
* @param string $id Form label.
* @return SimpleForm Form object containing the matching ID.
* @access public
*/
function &getFormById($id) {
for ($i = 0; $i < count($this->_complete_forms); $i++) {
if ($this->_complete_forms[$i]->getId() == $id) {
return $this->_complete_forms[$i];
}
}
$null = null;
return $null;
}
/**
* Sets a field on each form in which the field is
* available.
* @param SimpleSelector $selector Field finder.
* @param string $value Value to set field to.
* @return boolean True if value is valid.
* @access public
*/
function setField($selector, $value, $position=false) {
$is_set = false;
for ($i = 0; $i < count($this->_complete_forms); $i++) {
if ($this->_complete_forms[$i]->setField($selector, $value, $position)) {
$is_set = true;
}
}
return $is_set;
}
/**
* Accessor for a form element value within a page.
* @param SimpleSelector $selector Field finder.
* @return string/boolean A string if the field is
* present, false if unchecked
* and null if missing.
* @access public
*/
function getField($selector) {
for ($i = 0; $i < count($this->_complete_forms); $i++) {
$value = $this->_complete_forms[$i]->getValue($selector);
if (isset($value)) {
return $value;
}
}
return null;
}
}
?>

764
thirdparty/simpletest/parser.php vendored Normal file
View File

@ -0,0 +1,764 @@
<?php
/**
* base include file for SimpleTest
* @package SimpleTest
* @subpackage MockObjects
* @version $Id: parser.php 1723 2008-04-08 00:34:10Z lastcraft $
*/
/**#@+
* Lexer mode stack constants
*/
foreach (array('LEXER_ENTER', 'LEXER_MATCHED',
'LEXER_UNMATCHED', 'LEXER_EXIT',
'LEXER_SPECIAL') as $i => $constant) {
if (! defined($constant)) {
define($constant, $i + 1);
}
}
/**#@-*/
/**
* Compounded regular expression. Any of
* the contained patterns could match and
* when one does, it's label is returned.
* @package SimpleTest
* @subpackage WebTester
*/
class ParallelRegex {
var $_patterns;
var $_labels;
var $_regex;
var $_case;
/**
* Constructor. Starts with no patterns.
* @param boolean $case True for case sensitive, false
* for insensitive.
* @access public
*/
function ParallelRegex($case) {
$this->_case = $case;
$this->_patterns = array();
$this->_labels = array();
$this->_regex = null;
}
/**
* Adds a pattern with an optional label.
* @param string $pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string $label Label of regex to be returned
* on a match.
* @access public
*/
function addPattern($pattern, $label = true) {
$count = count($this->_patterns);
$this->_patterns[$count] = $pattern;
$this->_labels[$count] = $label;
$this->_regex = null;
}
/**
* Attempts to match all patterns at once against
* a string.
* @param string $subject String to match against.
* @param string $match First matched portion of
* subject.
* @return boolean True on success.
* @access public
*/
function match($subject, &$match) {
if (count($this->_patterns) == 0) {
return false;
}
if (! preg_match($this->_getCompoundedRegex(), $subject, $matches)) {
$match = '';
return false;
}
$match = $matches[0];
for ($i = 1; $i < count($matches); $i++) {
if ($matches[$i]) {
return $this->_labels[$i - 1];
}
}
return true;
}
/**
* Compounds the patterns into a single
* regular expression separated with the
* "or" operator. Caches the regex.
* Will automatically escape (, ) and / tokens.
* @param array $patterns List of patterns in order.
* @access private
*/
function _getCompoundedRegex() {
if ($this->_regex == null) {
for ($i = 0, $count = count($this->_patterns); $i < $count; $i++) {
$this->_patterns[$i] = '(' . str_replace(
array('/', '(', ')'),
array('\/', '\(', '\)'),
$this->_patterns[$i]) . ')';
}
$this->_regex = "/" . implode("|", $this->_patterns) . "/" . $this->_getPerlMatchingFlags();
}
return $this->_regex;
}
/**
* Accessor for perl regex mode flags to use.
* @return string Perl regex flags.
* @access private
*/
function _getPerlMatchingFlags() {
return ($this->_case ? "msS" : "msSi");
}
}
/**
* States for a stack machine.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleStateStack {
var $_stack;
/**
* Constructor. Starts in named state.
* @param string $start Starting state name.
* @access public
*/
function SimpleStateStack($start) {
$this->_stack = array($start);
}
/**
* Accessor for current state.
* @return string State.
* @access public
*/
function getCurrent() {
return $this->_stack[count($this->_stack) - 1];
}
/**
* Adds a state to the stack and sets it
* to be the current state.
* @param string $state New state.
* @access public
*/
function enter($state) {
array_push($this->_stack, $state);
}
/**
* Leaves the current state and reverts
* to the previous one.
* @return boolean False if we drop off
* the bottom of the list.
* @access public
*/
function leave() {
if (count($this->_stack) == 1) {
return false;
}
array_pop($this->_stack);
return true;
}
}
/**
* Accepts text and breaks it into tokens.
* Some optimisation to make the sure the
* content is only scanned by the PHP regex
* parser once. Lexer modes must not start
* with leading underscores.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleLexer {
var $_regexes;
var $_parser;
var $_mode;
var $_mode_handlers;
var $_case;
/**
* Sets up the lexer in case insensitive matching
* by default.
* @param SimpleSaxParser $parser Handling strategy by
* reference.
* @param string $start Starting handler.
* @param boolean $case True for case sensitive.
* @access public
*/
function SimpleLexer(&$parser, $start = "accept", $case = false) {
$this->_case = $case;
$this->_regexes = array();
$this->_parser = &$parser;
$this->_mode = &new SimpleStateStack($start);
$this->_mode_handlers = array($start => $start);
}
/**
* Adds a token search pattern for a particular
* parsing mode. The pattern does not change the
* current mode.
* @param string $pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string $mode Should only apply this
* pattern when dealing with
* this type of input.
* @access public
*/
function addPattern($pattern, $mode = "accept") {
if (! isset($this->_regexes[$mode])) {
$this->_regexes[$mode] = new ParallelRegex($this->_case);
}
$this->_regexes[$mode]->addPattern($pattern);
if (! isset($this->_mode_handlers[$mode])) {
$this->_mode_handlers[$mode] = $mode;
}
}
/**
* Adds a pattern that will enter a new parsing
* mode. Useful for entering parenthesis, strings,
* tags, etc.
* @param string $pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string $mode Should only apply this
* pattern when dealing with
* this type of input.
* @param string $new_mode Change parsing to this new
* nested mode.
* @access public
*/
function addEntryPattern($pattern, $mode, $new_mode) {
if (! isset($this->_regexes[$mode])) {
$this->_regexes[$mode] = new ParallelRegex($this->_case);
}
$this->_regexes[$mode]->addPattern($pattern, $new_mode);
if (! isset($this->_mode_handlers[$new_mode])) {
$this->_mode_handlers[$new_mode] = $new_mode;
}
}
/**
* Adds a pattern that will exit the current mode
* and re-enter the previous one.
* @param string $pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string $mode Mode to leave.
* @access public
*/
function addExitPattern($pattern, $mode) {
if (! isset($this->_regexes[$mode])) {
$this->_regexes[$mode] = new ParallelRegex($this->_case);
}
$this->_regexes[$mode]->addPattern($pattern, "__exit");
if (! isset($this->_mode_handlers[$mode])) {
$this->_mode_handlers[$mode] = $mode;
}
}
/**
* Adds a pattern that has a special mode. Acts as an entry
* and exit pattern in one go, effectively calling a special
* parser handler for this token only.
* @param string $pattern Perl style regex, but ( and )
* lose the usual meaning.
* @param string $mode Should only apply this
* pattern when dealing with
* this type of input.
* @param string $special Use this mode for this one token.
* @access public
*/
function addSpecialPattern($pattern, $mode, $special) {
if (! isset($this->_regexes[$mode])) {
$this->_regexes[$mode] = new ParallelRegex($this->_case);
}
$this->_regexes[$mode]->addPattern($pattern, "_$special");
if (! isset($this->_mode_handlers[$special])) {
$this->_mode_handlers[$special] = $special;
}
}
/**
* Adds a mapping from a mode to another handler.
* @param string $mode Mode to be remapped.
* @param string $handler New target handler.
* @access public
*/
function mapHandler($mode, $handler) {
$this->_mode_handlers[$mode] = $handler;
}
/**
* Splits the page text into tokens. Will fail
* if the handlers report an error or if no
* content is consumed. If successful then each
* unparsed and parsed token invokes a call to the
* held listener.
* @param string $raw Raw HTML text.
* @return boolean True on success, else false.
* @access public
*/
function parse($raw) {
if (! isset($this->_parser)) {
return false;
}
$length = strlen($raw);
while (is_array($parsed = $this->_reduce($raw))) {
list($raw, $unmatched, $matched, $mode) = $parsed;
if (! $this->_dispatchTokens($unmatched, $matched, $mode)) {
return false;
}
if ($raw === '') {
return true;
}
if (strlen($raw) == $length) {
return false;
}
$length = strlen($raw);
}
if (! $parsed) {
return false;
}
return $this->_invokeParser($raw, LEXER_UNMATCHED);
}
/**
* Sends the matched token and any leading unmatched
* text to the parser changing the lexer to a new
* mode if one is listed.
* @param string $unmatched Unmatched leading portion.
* @param string $matched Actual token match.
* @param string $mode Mode after match. A boolean
* false mode causes no change.
* @return boolean False if there was any error
* from the parser.
* @access private
*/
function _dispatchTokens($unmatched, $matched, $mode = false) {
if (! $this->_invokeParser($unmatched, LEXER_UNMATCHED)) {
return false;
}
if (is_bool($mode)) {
return $this->_invokeParser($matched, LEXER_MATCHED);
}
if ($this->_isModeEnd($mode)) {
if (! $this->_invokeParser($matched, LEXER_EXIT)) {
return false;
}
return $this->_mode->leave();
}
if ($this->_isSpecialMode($mode)) {
$this->_mode->enter($this->_decodeSpecial($mode));
if (! $this->_invokeParser($matched, LEXER_SPECIAL)) {
return false;
}
return $this->_mode->leave();
}
$this->_mode->enter($mode);
return $this->_invokeParser($matched, LEXER_ENTER);
}
/**
* Tests to see if the new mode is actually to leave
* the current mode and pop an item from the matching
* mode stack.
* @param string $mode Mode to test.
* @return boolean True if this is the exit mode.
* @access private
*/
function _isModeEnd($mode) {
return ($mode === "__exit");
}
/**
* Test to see if the mode is one where this mode
* is entered for this token only and automatically
* leaves immediately afterwoods.
* @param string $mode Mode to test.
* @return boolean True if this is the exit mode.
* @access private
*/
function _isSpecialMode($mode) {
return (strncmp($mode, "_", 1) == 0);
}
/**
* Strips the magic underscore marking single token
* modes.
* @param string $mode Mode to decode.
* @return string Underlying mode name.
* @access private
*/
function _decodeSpecial($mode) {
return substr($mode, 1);
}
/**
* Calls the parser method named after the current
* mode. Empty content will be ignored. The lexer
* has a parser handler for each mode in the lexer.
* @param string $content Text parsed.
* @param boolean $is_match Token is recognised rather
* than unparsed data.
* @access private
*/
function _invokeParser($content, $is_match) {
if (($content === '') || ($content === false)) {
return true;
}
$handler = $this->_mode_handlers[$this->_mode->getCurrent()];
return $this->_parser->$handler($content, $is_match);
}
/**
* Tries to match a chunk of text and if successful
* removes the recognised chunk and any leading
* unparsed data. Empty strings will not be matched.
* @param string $raw The subject to parse. This is the
* content that will be eaten.
* @return array/boolean Three item list of unparsed
* content followed by the
* recognised token and finally the
* action the parser is to take.
* True if no match, false if there
* is a parsing error.
* @access private
*/
function _reduce($raw) {
if ($action = $this->_regexes[$this->_mode->getCurrent()]->match($raw, $match)) {
$unparsed_character_count = strpos($raw, $match);
$unparsed = substr($raw, 0, $unparsed_character_count);
$raw = substr($raw, $unparsed_character_count + strlen($match));
return array($raw, $unparsed, $match, $action);
}
return true;
}
}
/**
* Breaks HTML into SAX events.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleHtmlLexer extends SimpleLexer {
/**
* Sets up the lexer with case insensitive matching
* and adds the HTML handlers.
* @param SimpleSaxParser $parser Handling strategy by
* reference.
* @access public
*/
function SimpleHtmlLexer(&$parser) {
$this->SimpleLexer($parser, 'text');
$this->mapHandler('text', 'acceptTextToken');
$this->_addSkipping();
foreach ($this->_getParsedTags() as $tag) {
$this->_addTag($tag);
}
$this->_addInTagTokens();
}
/**
* List of parsed tags. Others are ignored.
* @return array List of searched for tags.
* @access private
*/
function _getParsedTags() {
return array('a', 'base', 'title', 'form', 'input', 'button', 'textarea', 'select',
'option', 'frameset', 'frame', 'label');
}
/**
* The lexer has to skip certain sections such
* as server code, client code and styles.
* @access private
*/
function _addSkipping() {
$this->mapHandler('css', 'ignore');
$this->addEntryPattern('<style', 'text', 'css');
$this->addExitPattern('</style>', 'css');
$this->mapHandler('js', 'ignore');
$this->addEntryPattern('<script', 'text', 'js');
$this->addExitPattern('</script>', 'js');
$this->mapHandler('comment', 'ignore');
$this->addEntryPattern('<!--', 'text', 'comment');
$this->addExitPattern('-->', 'comment');
}
/**
* Pattern matches to start and end a tag.
* @param string $tag Name of tag to scan for.
* @access private
*/
function _addTag($tag) {
$this->addSpecialPattern("</$tag>", 'text', 'acceptEndToken');
$this->addEntryPattern("<$tag", 'text', 'tag');
}
/**
* Pattern matches to parse the inside of a tag
* including the attributes and their quoting.
* @access private
*/
function _addInTagTokens() {
$this->mapHandler('tag', 'acceptStartToken');
$this->addSpecialPattern('\s+', 'tag', 'ignore');
$this->_addAttributeTokens();
$this->addExitPattern('/>', 'tag');
$this->addExitPattern('>', 'tag');
}
/**
* Matches attributes that are either single quoted,
* double quoted or unquoted.
* @access private
*/
function _addAttributeTokens() {
$this->mapHandler('dq_attribute', 'acceptAttributeToken');
$this->addEntryPattern('=\s*"', 'tag', 'dq_attribute');
$this->addPattern("\\\\\"", 'dq_attribute');
$this->addExitPattern('"', 'dq_attribute');
$this->mapHandler('sq_attribute', 'acceptAttributeToken');
$this->addEntryPattern("=\s*'", 'tag', 'sq_attribute');
$this->addPattern("\\\\'", 'sq_attribute');
$this->addExitPattern("'", 'sq_attribute');
$this->mapHandler('uq_attribute', 'acceptAttributeToken');
$this->addSpecialPattern('=\s*[^>\s]*', 'tag', 'uq_attribute');
}
}
/**
* Converts HTML tokens into selected SAX events.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleHtmlSaxParser {
var $_lexer;
var $_listener;
var $_tag;
var $_attributes;
var $_current_attribute;
/**
* Sets the listener.
* @param SimpleSaxListener $listener SAX event handler.
* @access public
*/
function SimpleHtmlSaxParser(&$listener) {
$this->_listener = &$listener;
$this->_lexer = &$this->createLexer($this);
$this->_tag = '';
$this->_attributes = array();
$this->_current_attribute = '';
}
/**
* Runs the content through the lexer which
* should call back to the acceptors.
* @param string $raw Page text to parse.
* @return boolean False if parse error.
* @access public
*/
function parse($raw) {
return $this->_lexer->parse($raw);
}
/**
* Sets up the matching lexer. Starts in 'text' mode.
* @param SimpleSaxParser $parser Event generator, usually $self.
* @return SimpleLexer Lexer suitable for this parser.
* @access public
* @static
*/
function &createLexer(&$parser) {
$lexer = &new SimpleHtmlLexer($parser);
return $lexer;
}
/**
* Accepts a token from the tag mode. If the
* starting element completes then the element
* is dispatched and the current attributes
* set back to empty. The element or attribute
* name is converted to lower case.
* @param string $token Incoming characters.
* @param integer $event Lexer event type.
* @return boolean False if parse error.
* @access public
*/
function acceptStartToken($token, $event) {
if ($event == LEXER_ENTER) {
$this->_tag = strtolower(substr($token, 1));
return true;
}
if ($event == LEXER_EXIT) {
$success = $this->_listener->startElement(
$this->_tag,
$this->_attributes);
$this->_tag = '';
$this->_attributes = array();
return $success;
}
if ($token != '=') {
$this->_current_attribute = strtolower(SimpleHtmlSaxParser::decodeHtml($token));
$this->_attributes[$this->_current_attribute] = '';
}
return true;
}
/**
* Accepts a token from the end tag mode.
* The element name is converted to lower case.
* @param string $token Incoming characters.
* @param integer $event Lexer event type.
* @return boolean False if parse error.
* @access public
*/
function acceptEndToken($token, $event) {
if (! preg_match('/<\/(.*)>/', $token, $matches)) {
return false;
}
return $this->_listener->endElement(strtolower($matches[1]));
}
/**
* Part of the tag data.
* @param string $token Incoming characters.
* @param integer $event Lexer event type.
* @return boolean False if parse error.
* @access public
*/
function acceptAttributeToken($token, $event) {
if ($this->_current_attribute) {
if ($event == LEXER_UNMATCHED) {
$this->_attributes[$this->_current_attribute] .=
SimpleHtmlSaxParser::decodeHtml($token);
}
if ($event == LEXER_SPECIAL) {
$this->_attributes[$this->_current_attribute] .=
preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token));
}
}
return true;
}
/**
* A character entity.
* @param string $token Incoming characters.
* @param integer $event Lexer event type.
* @return boolean False if parse error.
* @access public
*/
function acceptEntityToken($token, $event) {
}
/**
* Character data between tags regarded as
* important.
* @param string $token Incoming characters.
* @param integer $event Lexer event type.
* @return boolean False if parse error.
* @access public
*/
function acceptTextToken($token, $event) {
return $this->_listener->addContent($token);
}
/**
* Incoming data to be ignored.
* @param string $token Incoming characters.
* @param integer $event Lexer event type.
* @return boolean False if parse error.
* @access public
*/
function ignore($token, $event) {
return true;
}
/**
* Decodes any HTML entities.
* @param string $html Incoming HTML.
* @return string Outgoing plain text.
* @access public
* @static
*/
function decodeHtml($html) {
return html_entity_decode($html, ENT_QUOTES);
}
/**
* Turns HTML into text browser visible text. Images
* are converted to their alt text and tags are supressed.
* Entities are converted to their visible representation.
* @param string $html HTML to convert.
* @return string Plain text.
* @access public
* @static
*/
function normalise($html) {
$text = preg_replace('|<!--.*?-->|', '', $html);
$text = preg_replace('|<script[^>]*>.*?</script>|', '', $text);
$text = preg_replace('|<img[^>]*alt\s*=\s*"([^"]*)"[^>]*>|', ' \1 ', $text);
$text = preg_replace('|<img[^>]*alt\s*=\s*\'([^\']*)\'[^>]*>|', ' \1 ', $text);
$text = preg_replace('|<img[^>]*alt\s*=\s*([a-zA-Z_]+)[^>]*>|', ' \1 ', $text);
$text = preg_replace('|<[^>]*>|', '', $text);
$text = SimpleHtmlSaxParser::decodeHtml($text);
$text = preg_replace('|\s+|', ' ', $text);
return trim(trim($text), "\xA0"); // TODO: The \xAO is a &nbsp;. Add a test for this.
}
}
/**
* SAX event handler.
* @package SimpleTest
* @subpackage WebTester
* @abstract
*/
class SimpleSaxListener {
/**
* Sets the document to write to.
* @access public
*/
function SimpleSaxListener() {
}
/**
* Start of element event.
* @param string $name Element name.
* @param hash $attributes Name value pairs.
* Attributes without content
* are marked as true.
* @return boolean False on parse error.
* @access public
*/
function startElement($name, $attributes) {
}
/**
* End of element event.
* @param string $name Element name.
* @return boolean False on parse error.
* @access public
*/
function endElement($name) {
}
/**
* Unparsed, but relevant data.
* @param string $text May include unparsed tags.
* @return boolean False on parse error.
* @access public
*/
function addContent($text) {
}
}
?>

137
thirdparty/simpletest/selector.php vendored Normal file
View File

@ -0,0 +1,137 @@
<?php
/**
* Base include file for SimpleTest.
* @package SimpleTest
* @subpackage WebTester
* @version $Id: selector.php 1723 2008-04-08 00:34:10Z lastcraft $
*/
/**#@+
* include SimpleTest files
*/
require_once(dirname(__FILE__) . '/tag.php');
require_once(dirname(__FILE__) . '/encoding.php');
/**#@-*/
/**
* Used to extract form elements for testing against.
* Searches by name attribute.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleByName {
var $_name;
/**
* Stashes the name for later comparison.
* @param string $name Name attribute to match.
*/
function SimpleByName($name) {
$this->_name = $name;
}
function getName() {
return $this->_name;
}
/**
* Compares with name attribute of widget.
* @param SimpleWidget $widget Control to compare.
* @access public
*/
function isMatch($widget) {
return ($widget->getName() == $this->_name);
}
}
/**
* Used to extract form elements for testing against.
* Searches by visible label or alt text.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleByLabel {
var $_label;
/**
* Stashes the name for later comparison.
* @param string $label Visible text to match.
*/
function SimpleByLabel($label) {
$this->_label = $label;
}
/**
* Comparison. Compares visible text of widget or
* related label.
* @param SimpleWidget $widget Control to compare.
* @access public
*/
function isMatch($widget) {
if (! method_exists($widget, 'isLabel')) {
return false;
}
return $widget->isLabel($this->_label);
}
}
/**
* Used to extract form elements for testing against.
* Searches dy id attribute.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleById {
var $_id;
/**
* Stashes the name for later comparison.
* @param string $id ID atribute to match.
*/
function SimpleById($id) {
$this->_id = $id;
}
/**
* Comparison. Compares id attribute of widget.
* @param SimpleWidget $widget Control to compare.
* @access public
*/
function isMatch($widget) {
return $widget->isId($this->_id);
}
}
/**
* Used to extract form elements for testing against.
* Searches by visible label, name or alt text.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleByLabelOrName {
var $_label;
/**
* Stashes the name/label for later comparison.
* @param string $label Visible text to match.
*/
function SimpleByLabelOrName($label) {
$this->_label = $label;
}
/**
* Comparison. Compares visible text of widget or
* related label or name.
* @param SimpleWidget $widget Control to compare.
* @access public
*/
function isMatch($widget) {
if (method_exists($widget, 'isLabel')) {
if ($widget->isLabel($this->_label)) {
return true;
}
}
return ($widget->getName() == $this->_label);
}
}
?>

216
thirdparty/simpletest/socket.php vendored Normal file
View File

@ -0,0 +1,216 @@
<?php
/**
* base include file for SimpleTest
* @package SimpleTest
* @subpackage MockObjects
* @version $Id: socket.php 1723 2008-04-08 00:34:10Z lastcraft $
*/
/**#@+
* include SimpleTest files
*/
require_once(dirname(__FILE__) . '/compatibility.php');
/**#@-*/
/**
* Stashes an error for later. Useful for constructors
* until PHP gets exceptions.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleStickyError {
var $_error = 'Constructor not chained';
/**
* Sets the error to empty.
* @access public
*/
function SimpleStickyError() {
$this->_clearError();
}
/**
* Test for an outstanding error.
* @return boolean True if there is an error.
* @access public
*/
function isError() {
return ($this->_error != '');
}
/**
* Accessor for an outstanding error.
* @return string Empty string if no error otherwise
* the error message.
* @access public
*/
function getError() {
return $this->_error;
}
/**
* Sets the internal error.
* @param string Error message to stash.
* @access protected
*/
function _setError($error) {
$this->_error = $error;
}
/**
* Resets the error state to no error.
* @access protected
*/
function _clearError() {
$this->_setError('');
}
}
/**
* Wrapper for TCP/IP socket.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleSocket extends SimpleStickyError {
var $_handle;
var $_is_open = false;
var $_sent = '';
var $lock_size;
/**
* Opens a socket for reading and writing.
* @param string $host Hostname to send request to.
* @param integer $port Port on remote machine to open.
* @param integer $timeout Connection timeout in seconds.
* @param integer $block_size Size of chunk to read.
* @access public
*/
function SimpleSocket($host, $port, $timeout, $block_size = 255) {
$this->SimpleStickyError();
if (! ($this->_handle = $this->_openSocket($host, $port, $error_number, $error, $timeout))) {
$this->_setError("Cannot open [$host:$port] with [$error] within [$timeout] seconds");
return;
}
$this->_is_open = true;
$this->_block_size = $block_size;
SimpleTestCompatibility::setTimeout($this->_handle, $timeout);
}
/**
* Writes some data to the socket and saves alocal copy.
* @param string $message String to send to socket.
* @return boolean True if successful.
* @access public
*/
function write($message) {
if ($this->isError() || ! $this->isOpen()) {
return false;
}
$count = fwrite($this->_handle, $message);
if (! $count) {
if ($count === false) {
$this->_setError('Cannot write to socket');
$this->close();
}
return false;
}
fflush($this->_handle);
$this->_sent .= $message;
return true;
}
/**
* Reads data from the socket. The error suppresion
* is a workaround for PHP4 always throwing a warning
* with a secure socket.
* @return integer/boolean Incoming bytes. False
* on error.
* @access public
*/
function read() {
if ($this->isError() || ! $this->isOpen()) {
return false;
}
$raw = @fread($this->_handle, $this->_block_size);
if ($raw === false) {
$this->_setError('Cannot read from socket');
$this->close();
}
return $raw;
}
/**
* Accessor for socket open state.
* @return boolean True if open.
* @access public
*/
function isOpen() {
return $this->_is_open;
}
/**
* Closes the socket preventing further reads.
* Cannot be reopened once closed.
* @return boolean True if successful.
* @access public
*/
function close() {
$this->_is_open = false;
return fclose($this->_handle);
}
/**
* Accessor for content so far.
* @return string Bytes sent only.
* @access public
*/
function getSent() {
return $this->_sent;
}
/**
* Actually opens the low level socket.
* @param string $host Host to connect to.
* @param integer $port Port on host.
* @param integer $error_number Recipient of error code.
* @param string $error Recipoent of error message.
* @param integer $timeout Maximum time to wait for connection.
* @access protected
*/
function _openSocket($host, $port, &$error_number, &$error, $timeout) {
return @fsockopen($host, $port, $error_number, $error, $timeout);
}
}
/**
* Wrapper for TCP/IP socket over TLS.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleSecureSocket extends SimpleSocket {
/**
* Opens a secure socket for reading and writing.
* @param string $host Hostname to send request to.
* @param integer $port Port on remote machine to open.
* @param integer $timeout Connection timeout in seconds.
* @access public
*/
function SimpleSecureSocket($host, $port, $timeout) {
$this->SimpleSocket($host, $port, $timeout);
}
/**
* Actually opens the low level socket.
* @param string $host Host to connect to.
* @param integer $port Port on host.
* @param integer $error_number Recipient of error code.
* @param string $error Recipient of error message.
* @param integer $timeout Maximum time to wait for connection.
* @access protected
*/
function _openSocket($host, $port, &$error_number, &$error, $timeout) {
return parent::_openSocket("tls://$host", $port, $error_number, $error, $timeout);
}
}
?>

1418
thirdparty/simpletest/tag.php vendored Normal file
View File

@ -0,0 +1,1418 @@
<?php
/**
* Base include file for SimpleTest.
* @package SimpleTest
* @subpackage WebTester
* @version $Id: tag.php 1723 2008-04-08 00:34:10Z lastcraft $
*/
/**#@+
* include SimpleTest files
*/
require_once(dirname(__FILE__) . '/parser.php');
require_once(dirname(__FILE__) . '/encoding.php');
/**#@-*/
/**
* HTML or XML tag.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleTag {
var $_name;
var $_attributes;
var $_content;
/**
* Starts with a named tag with attributes only.
* @param string $name Tag name.
* @param hash $attributes Attribute names and
* string values. Note that
* the keys must have been
* converted to lower case.
*/
function SimpleTag($name, $attributes) {
$this->_name = strtolower(trim($name));
$this->_attributes = $attributes;
$this->_content = '';
}
/**
* Check to see if the tag can have both start and
* end tags with content in between.
* @return boolean True if content allowed.
* @access public
*/
function expectEndTag() {
return true;
}
/**
* The current tag should not swallow all content for
* itself as it's searchable page content. Private
* content tags are usually widgets that contain default
* values.
* @return boolean False as content is available
* to other tags by default.
* @access public
*/
function isPrivateContent() {
return false;
}
/**
* Appends string content to the current content.
* @param string $content Additional text.
* @access public
*/
function addContent($content) {
$this->_content .= (string)$content;
}
/**
* Adds an enclosed tag to the content.
* @param SimpleTag $tag New tag.
* @access public
*/
function addTag(&$tag) {
}
/**
* Accessor for tag name.
* @return string Name of tag.
* @access public
*/
function getTagName() {
return $this->_name;
}
/**
* List of legal child elements.
* @return array List of element names.
* @access public
*/
function getChildElements() {
return array();
}
/**
* Accessor for an attribute.
* @param string $label Attribute name.
* @return string Attribute value.
* @access public
*/
function getAttribute($label) {
$label = strtolower($label);
if (! isset($this->_attributes[$label])) {
return false;
}
return (string)$this->_attributes[$label];
}
/**
* Sets an attribute.
* @param string $label Attribute name.
* @return string $value New attribute value.
* @access protected
*/
function _setAttribute($label, $value) {
$this->_attributes[strtolower($label)] = $value;
}
/**
* Accessor for the whole content so far.
* @return string Content as big raw string.
* @access public
*/
function getContent() {
return $this->_content;
}
/**
* Accessor for content reduced to visible text. Acts
* like a text mode browser, normalising space and
* reducing images to their alt text.
* @return string Content as plain text.
* @access public
*/
function getText() {
return SimpleHtmlSaxParser::normalise($this->_content);
}
/**
* Test to see if id attribute matches.
* @param string $id ID to test against.
* @return boolean True on match.
* @access public
*/
function isId($id) {
return ($this->getAttribute('id') == $id);
}
}
/**
* Base url.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleBaseTag extends SimpleTag {
/**
* Starts with a named tag with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleBaseTag($attributes) {
$this->SimpleTag('base', $attributes);
}
/**
* Base tag is not a block tag.
* @return boolean false
* @access public
*/
function expectEndTag() {
return false;
}
}
/**
* Page title.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleTitleTag extends SimpleTag {
/**
* Starts with a named tag with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleTitleTag($attributes) {
$this->SimpleTag('title', $attributes);
}
}
/**
* Link.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleAnchorTag extends SimpleTag {
/**
* Starts with a named tag with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleAnchorTag($attributes) {
$this->SimpleTag('a', $attributes);
}
/**
* Accessor for URL as string.
* @return string Coerced as string.
* @access public
*/
function getHref() {
$url = $this->getAttribute('href');
if (is_bool($url)) {
$url = '';
}
return $url;
}
}
/**
* Form element.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleWidget extends SimpleTag {
var $_value;
var $_label;
var $_is_set;
/**
* Starts with a named tag with attributes only.
* @param string $name Tag name.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleWidget($name, $attributes) {
$this->SimpleTag($name, $attributes);
$this->_value = false;
$this->_label = false;
$this->_is_set = false;
}
/**
* Accessor for name submitted as the key in
* GET/POST variables hash.
* @return string Parsed value.
* @access public
*/
function getName() {
return $this->getAttribute('name');
}
/**
* Accessor for default value parsed with the tag.
* @return string Parsed value.
* @access public
*/
function getDefault() {
return $this->getAttribute('value');
}
/**
* Accessor for currently set value or default if
* none.
* @return string Value set by form or default
* if none.
* @access public
*/
function getValue() {
if (! $this->_is_set) {
return $this->getDefault();
}
return $this->_value;
}
/**
* Sets the current form element value.
* @param string $value New value.
* @return boolean True if allowed.
* @access public
*/
function setValue($value) {
$this->_value = $value;
$this->_is_set = true;
return true;
}
/**
* Resets the form element value back to the
* default.
* @access public
*/
function resetValue() {
$this->_is_set = false;
}
/**
* Allows setting of a label externally, say by a
* label tag.
* @param string $label Label to attach.
* @access public
*/
function setLabel($label) {
$this->_label = trim($label);
}
/**
* Reads external or internal label.
* @param string $label Label to test.
* @return boolean True is match.
* @access public
*/
function isLabel($label) {
return $this->_label == trim($label);
}
/**
* Dispatches the value into the form encoded packet.
* @param SimpleEncoding $encoding Form packet.
* @access public
*/
function write(&$encoding) {
if ($this->getName()) {
$encoding->add($this->getName(), $this->getValue());
}
}
}
/**
* Text, password and hidden field.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleTextTag extends SimpleWidget {
/**
* Starts with a named tag with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleTextTag($attributes) {
$this->SimpleWidget('input', $attributes);
if ($this->getAttribute('value') === false) {
$this->_setAttribute('value', '');
}
}
/**
* Tag contains no content.
* @return boolean False.
* @access public
*/
function expectEndTag() {
return false;
}
/**
* Sets the current form element value. Cannot
* change the value of a hidden field.
* @param string $value New value.
* @return boolean True if allowed.
* @access public
*/
function setValue($value) {
if ($this->getAttribute('type') == 'hidden') {
return false;
}
return parent::setValue($value);
}
}
/**
* Submit button as input tag.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleSubmitTag extends SimpleWidget {
/**
* Starts with a named tag with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleSubmitTag($attributes) {
$this->SimpleWidget('input', $attributes);
if ($this->getAttribute('value') === false) {
$this->_setAttribute('value', 'Submit');
}
}
/**
* Tag contains no end element.
* @return boolean False.
* @access public
*/
function expectEndTag() {
return false;
}
/**
* Disables the setting of the button value.
* @param string $value Ignored.
* @return boolean True if allowed.
* @access public
*/
function setValue($value) {
return false;
}
/**
* Value of browser visible text.
* @return string Visible label.
* @access public
*/
function getLabel() {
return $this->getValue();
}
/**
* Test for a label match when searching.
* @param string $label Label to test.
* @return boolean True on match.
* @access public
*/
function isLabel($label) {
return trim($label) == trim($this->getLabel());
}
}
/**
* Image button as input tag.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleImageSubmitTag extends SimpleWidget {
/**
* Starts with a named tag with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleImageSubmitTag($attributes) {
$this->SimpleWidget('input', $attributes);
}
/**
* Tag contains no end element.
* @return boolean False.
* @access public
*/
function expectEndTag() {
return false;
}
/**
* Disables the setting of the button value.
* @param string $value Ignored.
* @return boolean True if allowed.
* @access public
*/
function setValue($value) {
return false;
}
/**
* Value of browser visible text.
* @return string Visible label.
* @access public
*/
function getLabel() {
if ($this->getAttribute('title')) {
return $this->getAttribute('title');
}
return $this->getAttribute('alt');
}
/**
* Test for a label match when searching.
* @param string $label Label to test.
* @return boolean True on match.
* @access public
*/
function isLabel($label) {
return trim($label) == trim($this->getLabel());
}
/**
* Dispatches the value into the form encoded packet.
* @param SimpleEncoding $encoding Form packet.
* @param integer $x X coordinate of click.
* @param integer $y Y coordinate of click.
* @access public
*/
function write(&$encoding, $x, $y) {
if ($this->getName()) {
$encoding->add($this->getName() . '.x', $x);
$encoding->add($this->getName() . '.y', $y);
} else {
$encoding->add('x', $x);
$encoding->add('y', $y);
}
}
}
/**
* Submit button as button tag.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleButtonTag extends SimpleWidget {
/**
* Starts with a named tag with attributes only.
* Defaults are very browser dependent.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleButtonTag($attributes) {
$this->SimpleWidget('button', $attributes);
}
/**
* Check to see if the tag can have both start and
* end tags with content in between.
* @return boolean True if content allowed.
* @access public
*/
function expectEndTag() {
return true;
}
/**
* Disables the setting of the button value.
* @param string $value Ignored.
* @return boolean True if allowed.
* @access public
*/
function setValue($value) {
return false;
}
/**
* Value of browser visible text.
* @return string Visible label.
* @access public
*/
function getLabel() {
return $this->getContent();
}
/**
* Test for a label match when searching.
* @param string $label Label to test.
* @return boolean True on match.
* @access public
*/
function isLabel($label) {
return trim($label) == trim($this->getLabel());
}
}
/**
* Content tag for text area.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleTextAreaTag extends SimpleWidget {
/**
* Starts with a named tag with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleTextAreaTag($attributes) {
$this->SimpleWidget('textarea', $attributes);
}
/**
* Accessor for starting value.
* @return string Parsed value.
* @access public
*/
function getDefault() {
return $this->_wrap(SimpleHtmlSaxParser::decodeHtml($this->getContent()));
}
/**
* Applies word wrapping if needed.
* @param string $value New value.
* @return boolean True if allowed.
* @access public
*/
function setValue($value) {
return parent::setValue($this->_wrap($value));
}
/**
* Test to see if text should be wrapped.
* @return boolean True if wrapping on.
* @access private
*/
function _wrapIsEnabled() {
if ($this->getAttribute('cols')) {
$wrap = $this->getAttribute('wrap');
if (($wrap == 'physical') || ($wrap == 'hard')) {
return true;
}
}
return false;
}
/**
* Performs the formatting that is peculiar to
* this tag. There is strange behaviour in this
* one, including stripping a leading new line.
* Go figure. I am using Firefox as a guide.
* @param string $text Text to wrap.
* @return string Text wrapped with carriage
* returns and line feeds
* @access private
*/
function _wrap($text) {
$text = str_replace("\r\r\n", "\r\n", str_replace("\n", "\r\n", $text));
$text = str_replace("\r\n\n", "\r\n", str_replace("\r", "\r\n", $text));
if (strncmp($text, "\r\n", strlen("\r\n")) == 0) {
$text = substr($text, strlen("\r\n"));
}
if ($this->_wrapIsEnabled()) {
return wordwrap(
$text,
(integer)$this->getAttribute('cols'),
"\r\n");
}
return $text;
}
/**
* The content of textarea is not part of the page.
* @return boolean True.
* @access public
*/
function isPrivateContent() {
return true;
}
}
/**
* File upload widget.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleUploadTag extends SimpleWidget {
/**
* Starts with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleUploadTag($attributes) {
$this->SimpleWidget('input', $attributes);
}
/**
* Tag contains no content.
* @return boolean False.
* @access public
*/
function expectEndTag() {
return false;
}
/**
* Dispatches the value into the form encoded packet.
* @param SimpleEncoding $encoding Form packet.
* @access public
*/
function write(&$encoding) {
if (! file_exists($this->getValue())) {
return;
}
$encoding->attach(
$this->getName(),
implode('', file($this->getValue())),
basename($this->getValue()));
}
}
/**
* Drop down widget.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleSelectionTag extends SimpleWidget {
var $_options;
var $_choice;
/**
* Starts with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleSelectionTag($attributes) {
$this->SimpleWidget('select', $attributes);
$this->_options = array();
$this->_choice = false;
}
/**
* Adds an option tag to a selection field.
* @param SimpleOptionTag $tag New option.
* @access public
*/
function addTag(&$tag) {
if ($tag->getTagName() == 'option') {
$this->_options[] = &$tag;
}
}
/**
* Text within the selection element is ignored.
* @param string $content Ignored.
* @access public
*/
function addContent($content) {
}
/**
* Scans options for defaults. If none, then
* the first option is selected.
* @return string Selected field.
* @access public
*/
function getDefault() {
for ($i = 0, $count = count($this->_options); $i < $count; $i++) {
if ($this->_options[$i]->getAttribute('selected') !== false) {
return $this->_options[$i]->getDefault();
}
}
if ($count > 0) {
return $this->_options[0]->getDefault();
}
return '';
}
/**
* Can only set allowed values.
* @param string $value New choice.
* @return boolean True if allowed.
* @access public
*/
function setValue($value) {
for ($i = 0, $count = count($this->_options); $i < $count; $i++) {
if ($this->_options[$i]->isValue($value)) {
$this->_choice = $i;
return true;
}
}
return false;
}
/**
* Accessor for current selection value.
* @return string Value attribute or
* content of opton.
* @access public
*/
function getValue() {
if ($this->_choice === false) {
return $this->getDefault();
}
return $this->_options[$this->_choice]->getValue();
}
}
/**
* Drop down widget.
* @package SimpleTest
* @subpackage WebTester
*/
class MultipleSelectionTag extends SimpleWidget {
var $_options;
var $_values;
/**
* Starts with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function MultipleSelectionTag($attributes) {
$this->SimpleWidget('select', $attributes);
$this->_options = array();
$this->_values = false;
}
/**
* Adds an option tag to a selection field.
* @param SimpleOptionTag $tag New option.
* @access public
*/
function addTag(&$tag) {
if ($tag->getTagName() == 'option') {
$this->_options[] = &$tag;
}
}
/**
* Text within the selection element is ignored.
* @param string $content Ignored.
* @access public
*/
function addContent($content) {
}
/**
* Scans options for defaults to populate the
* value array().
* @return array Selected fields.
* @access public
*/
function getDefault() {
$default = array();
for ($i = 0, $count = count($this->_options); $i < $count; $i++) {
if ($this->_options[$i]->getAttribute('selected') !== false) {
$default[] = $this->_options[$i]->getDefault();
}
}
return $default;
}
/**
* Can only set allowed values. Any illegal value
* will result in a failure, but all correct values
* will be set.
* @param array $desired New choices.
* @return boolean True if all allowed.
* @access public
*/
function setValue($desired) {
$achieved = array();
foreach ($desired as $value) {
$success = false;
for ($i = 0, $count = count($this->_options); $i < $count; $i++) {
if ($this->_options[$i]->isValue($value)) {
$achieved[] = $this->_options[$i]->getValue();
$success = true;
break;
}
}
if (! $success) {
return false;
}
}
$this->_values = $achieved;
return true;
}
/**
* Accessor for current selection value.
* @return array List of currently set options.
* @access public
*/
function getValue() {
if ($this->_values === false) {
return $this->getDefault();
}
return $this->_values;
}
}
/**
* Option for selection field.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleOptionTag extends SimpleWidget {
/**
* Stashes the attributes.
*/
function SimpleOptionTag($attributes) {
$this->SimpleWidget('option', $attributes);
}
/**
* Does nothing.
* @param string $value Ignored.
* @return boolean Not allowed.
* @access public
*/
function setValue($value) {
return false;
}
/**
* Test to see if a value matches the option.
* @param string $compare Value to compare with.
* @return boolean True if possible match.
* @access public
*/
function isValue($compare) {
$compare = trim($compare);
if (trim($this->getValue()) == $compare) {
return true;
}
return trim($this->getContent()) == $compare;
}
/**
* Accessor for starting value. Will be set to
* the option label if no value exists.
* @return string Parsed value.
* @access public
*/
function getDefault() {
if ($this->getAttribute('value') === false) {
return $this->getContent();
}
return $this->getAttribute('value');
}
/**
* The content of options is not part of the page.
* @return boolean True.
* @access public
*/
function isPrivateContent() {
return true;
}
}
/**
* Radio button.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleRadioButtonTag extends SimpleWidget {
/**
* Stashes the attributes.
* @param array $attributes Hash of attributes.
*/
function SimpleRadioButtonTag($attributes) {
$this->SimpleWidget('input', $attributes);
if ($this->getAttribute('value') === false) {
$this->_setAttribute('value', 'on');
}
}
/**
* Tag contains no content.
* @return boolean False.
* @access public
*/
function expectEndTag() {
return false;
}
/**
* The only allowed value sn the one in the
* "value" attribute.
* @param string $value New value.
* @return boolean True if allowed.
* @access public
*/
function setValue($value) {
if ($value === false) {
return parent::setValue($value);
}
if ($value != $this->getAttribute('value')) {
return false;
}
return parent::setValue($value);
}
/**
* Accessor for starting value.
* @return string Parsed value.
* @access public
*/
function getDefault() {
if ($this->getAttribute('checked') !== false) {
return $this->getAttribute('value');
}
return false;
}
}
/**
* Checkbox widget.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleCheckboxTag extends SimpleWidget {
/**
* Starts with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleCheckboxTag($attributes) {
$this->SimpleWidget('input', $attributes);
if ($this->getAttribute('value') === false) {
$this->_setAttribute('value', 'on');
}
}
/**
* Tag contains no content.
* @return boolean False.
* @access public
*/
function expectEndTag() {
return false;
}
/**
* The only allowed value in the one in the
* "value" attribute. The default for this
* attribute is "on". If this widget is set to
* true, then the usual value will be taken.
* @param string $value New value.
* @return boolean True if allowed.
* @access public
*/
function setValue($value) {
if ($value === false) {
return parent::setValue($value);
}
if ($value === true) {
return parent::setValue($this->getAttribute('value'));
}
if ($value != $this->getAttribute('value')) {
return false;
}
return parent::setValue($value);
}
/**
* Accessor for starting value. The default
* value is "on".
* @return string Parsed value.
* @access public
*/
function getDefault() {
if ($this->getAttribute('checked') !== false) {
return $this->getAttribute('value');
}
return false;
}
}
/**
* A group of multiple widgets with some shared behaviour.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleTagGroup {
var $_widgets = array();
/**
* Adds a tag to the group.
* @param SimpleWidget $widget
* @access public
*/
function addWidget(&$widget) {
$this->_widgets[] = &$widget;
}
/**
* Accessor to widget set.
* @return array All widgets.
* @access protected
*/
function &_getWidgets() {
return $this->_widgets;
}
/**
* Accessor for an attribute.
* @param string $label Attribute name.
* @return boolean Always false.
* @access public
*/
function getAttribute($label) {
return false;
}
/**
* Fetches the name for the widget from the first
* member.
* @return string Name of widget.
* @access public
*/
function getName() {
if (count($this->_widgets) > 0) {
return $this->_widgets[0]->getName();
}
}
/**
* Scans the widgets for one with the appropriate
* ID field.
* @param string $id ID value to try.
* @return boolean True if matched.
* @access public
*/
function isId($id) {
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
if ($this->_widgets[$i]->isId($id)) {
return true;
}
}
return false;
}
/**
* Scans the widgets for one with the appropriate
* attached label.
* @param string $label Attached label to try.
* @return boolean True if matched.
* @access public
*/
function isLabel($label) {
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
if ($this->_widgets[$i]->isLabel($label)) {
return true;
}
}
return false;
}
/**
* Dispatches the value into the form encoded packet.
* @param SimpleEncoding $encoding Form packet.
* @access public
*/
function write(&$encoding) {
$encoding->add($this->getName(), $this->getValue());
}
}
/**
* A group of tags with the same name within a form.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleCheckboxGroup extends SimpleTagGroup {
/**
* Accessor for current selected widget or false
* if none.
* @return string/array Widget values or false if none.
* @access public
*/
function getValue() {
$values = array();
$widgets = &$this->_getWidgets();
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
if ($widgets[$i]->getValue() !== false) {
$values[] = $widgets[$i]->getValue();
}
}
return $this->_coerceValues($values);
}
/**
* Accessor for starting value that is active.
* @return string/array Widget values or false if none.
* @access public
*/
function getDefault() {
$values = array();
$widgets = &$this->_getWidgets();
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
if ($widgets[$i]->getDefault() !== false) {
$values[] = $widgets[$i]->getDefault();
}
}
return $this->_coerceValues($values);
}
/**
* Accessor for current set values.
* @param string/array/boolean $values Either a single string, a
* hash or false for nothing set.
* @return boolean True if all values can be set.
* @access public
*/
function setValue($values) {
$values = $this->_makeArray($values);
if (! $this->_valuesArePossible($values)) {
return false;
}
$widgets = &$this->_getWidgets();
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
$possible = $widgets[$i]->getAttribute('value');
if (in_array($widgets[$i]->getAttribute('value'), $values)) {
$widgets[$i]->setValue($possible);
} else {
$widgets[$i]->setValue(false);
}
}
return true;
}
/**
* Tests to see if a possible value set is legal.
* @param string/array/boolean $values Either a single string, a
* hash or false for nothing set.
* @return boolean False if trying to set a
* missing value.
* @access private
*/
function _valuesArePossible($values) {
$matches = array();
$widgets = &$this->_getWidgets();
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
$possible = $widgets[$i]->getAttribute('value');
if (in_array($possible, $values)) {
$matches[] = $possible;
}
}
return ($values == $matches);
}
/**
* Converts the output to an appropriate format. This means
* that no values is false, a single value is just that
* value and only two or more are contained in an array.
* @param array $values List of values of widgets.
* @return string/array/boolean Expected format for a tag.
* @access private
*/
function _coerceValues($values) {
if (count($values) == 0) {
return false;
} elseif (count($values) == 1) {
return $values[0];
} else {
return $values;
}
}
/**
* Converts false or string into array. The opposite of
* the coercian method.
* @param string/array/boolean $value A single item is converted
* to a one item list. False
* gives an empty list.
* @return array List of values, possibly empty.
* @access private
*/
function _makeArray($value) {
if ($value === false) {
return array();
}
if (is_string($value)) {
return array($value);
}
return $value;
}
}
/**
* A group of tags with the same name within a form.
* Used for radio buttons.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleRadioGroup extends SimpleTagGroup {
/**
* Each tag is tried in turn until one is
* successfully set. The others will be
* unchecked if successful.
* @param string $value New value.
* @return boolean True if any allowed.
* @access public
*/
function setValue($value) {
if (! $this->_valueIsPossible($value)) {
return false;
}
$index = false;
$widgets = &$this->_getWidgets();
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
if (! $widgets[$i]->setValue($value)) {
$widgets[$i]->setValue(false);
}
}
return true;
}
/**
* Tests to see if a value is allowed.
* @param string Attempted value.
* @return boolean True if a valid value.
* @access private
*/
function _valueIsPossible($value) {
$widgets = &$this->_getWidgets();
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
if ($widgets[$i]->getAttribute('value') == $value) {
return true;
}
}
return false;
}
/**
* Accessor for current selected widget or false
* if none.
* @return string/boolean Value attribute or
* content of opton.
* @access public
*/
function getValue() {
$widgets = &$this->_getWidgets();
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
if ($widgets[$i]->getValue() !== false) {
return $widgets[$i]->getValue();
}
}
return false;
}
/**
* Accessor for starting value that is active.
* @return string/boolean Value of first checked
* widget or false if none.
* @access public
*/
function getDefault() {
$widgets = &$this->_getWidgets();
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
if ($widgets[$i]->getDefault() !== false) {
return $widgets[$i]->getDefault();
}
}
return false;
}
}
/**
* Tag to keep track of labels.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleLabelTag extends SimpleTag {
/**
* Starts with a named tag with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleLabelTag($attributes) {
$this->SimpleTag('label', $attributes);
}
/**
* Access for the ID to attach the label to.
* @return string For attribute.
* @access public
*/
function getFor() {
return $this->getAttribute('for');
}
}
/**
* Tag to aid parsing the form.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleFormTag extends SimpleTag {
/**
* Starts with a named tag with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleFormTag($attributes) {
$this->SimpleTag('form', $attributes);
}
}
/**
* Tag to aid parsing the frames in a page.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleFrameTag extends SimpleTag {
/**
* Starts with a named tag with attributes only.
* @param hash $attributes Attribute names and
* string values.
*/
function SimpleFrameTag($attributes) {
$this->SimpleTag('frame', $attributes);
}
/**
* Tag contains no content.
* @return boolean False.
* @access public
*/
function expectEndTag() {
return false;
}
}
?>

528
thirdparty/simpletest/url.php vendored Normal file
View File

@ -0,0 +1,528 @@
<?php
/**
* base include file for SimpleTest
* @package SimpleTest
* @subpackage WebTester
* @version $Id: url.php 1723 2008-04-08 00:34:10Z lastcraft $
*/
/**#@+
* include other SimpleTest class files
*/
require_once(dirname(__FILE__) . '/encoding.php');
/**#@-*/
/**
* URL parser to replace parse_url() PHP function which
* got broken in PHP 4.3.0. Adds some browser specific
* functionality such as expandomatics.
* Guesses a bit trying to separate the host from
* the path and tries to keep a raw, possibly unparsable,
* request string as long as possible.
* @package SimpleTest
* @subpackage WebTester
*/
class SimpleUrl {
var $_scheme;
var $_username;
var $_password;
var $_host;
var $_port;
var $_path;
var $_request;
var $_fragment;
var $_x;
var $_y;
var $_target;
var $_raw = false;
/**
* Constructor. Parses URL into sections.
* @param string $url Incoming URL.
* @access public
*/
function SimpleUrl($url = '') {
list($x, $y) = $this->_chompCoordinates($url);
$this->setCoordinates($x, $y);
$this->_scheme = $this->_chompScheme($url);
list($this->_username, $this->_password) = $this->_chompLogin($url);
$this->_host = $this->_chompHost($url);
$this->_port = false;
if (preg_match('/(.*?):(.*)/', $this->_host, $host_parts)) {
$this->_host = $host_parts[1];
$this->_port = (integer)$host_parts[2];
}
$this->_path = $this->_chompPath($url);
$this->_request = $this->_parseRequest($this->_chompRequest($url));
$this->_fragment = (strncmp($url, "#", 1) == 0 ? substr($url, 1) : false);
$this->_target = false;
}
/**
* Extracts the X, Y coordinate pair from an image map.
* @param string $url URL so far. The coordinates will be
* removed.
* @return array X, Y as a pair of integers.
* @access private
*/
function _chompCoordinates(&$url) {
if (preg_match('/(.*)\?(\d+),(\d+)$/', $url, $matches)) {
$url = $matches[1];
return array((integer)$matches[2], (integer)$matches[3]);
}
return array(false, false);
}
/**
* Extracts the scheme part of an incoming URL.
* @param string $url URL so far. The scheme will be
* removed.
* @return string Scheme part or false.
* @access private
*/
function _chompScheme(&$url) {
if (preg_match('/^([^\/:]*):(\/\/)(.*)/', $url, $matches)) {
$url = $matches[2] . $matches[3];
return $matches[1];
}
return false;
}
/**
* Extracts the username and password from the
* incoming URL. The // prefix will be reattached
* to the URL after the doublet is extracted.
* @param string $url URL so far. The username and
* password are removed.
* @return array Two item list of username and
* password. Will urldecode() them.
* @access private
*/
function _chompLogin(&$url) {
$prefix = '';
if (preg_match('/^(\/\/)(.*)/', $url, $matches)) {
$prefix = $matches[1];
$url = $matches[2];
}
if (preg_match('/^([^\/]*)@(.*)/', $url, $matches)) {
$url = $prefix . $matches[2];
$parts = split(":", $matches[1]);
return array(
urldecode($parts[0]),
isset($parts[1]) ? urldecode($parts[1]) : false);
}
$url = $prefix . $url;
return array(false, false);
}
/**
* Extracts the host part of an incoming URL.
* Includes the port number part. Will extract
* the host if it starts with // or it has
* a top level domain or it has at least two
* dots.
* @param string $url URL so far. The host will be
* removed.
* @return string Host part guess or false.
* @access private
*/
function _chompHost(&$url) {
if (preg_match('/^(\/\/)(.*?)(\/.*|\?.*|#.*|$)/', $url, $matches)) {
$url = $matches[3];
return $matches[2];
}
if (preg_match('/(.*?)(\.\.\/|\.\/|\/|\?|#|$)(.*)/', $url, $matches)) {
$tlds = SimpleUrl::getAllTopLevelDomains();
if (preg_match('/[a-z0-9\-]+\.(' . $tlds . ')/i', $matches[1])) {
$url = $matches[2] . $matches[3];
return $matches[1];
} elseif (preg_match('/[a-z0-9\-]+\.[a-z0-9\-]+\.[a-z0-9\-]+/i', $matches[1])) {
$url = $matches[2] . $matches[3];
return $matches[1];
}
}
return false;
}
/**
* Extracts the path information from the incoming
* URL. Strips this path from the URL.
* @param string $url URL so far. The host will be
* removed.
* @return string Path part or '/'.
* @access private
*/
function _chompPath(&$url) {
if (preg_match('/(.*?)(\?|#|$)(.*)/', $url, $matches)) {
$url = $matches[2] . $matches[3];
return ($matches[1] ? $matches[1] : '');
}
return '';
}
/**
* Strips off the request data.
* @param string $url URL so far. The request will be
* removed.
* @return string Raw request part.
* @access private
*/
function _chompRequest(&$url) {
if (preg_match('/\?(.*?)(#|$)(.*)/', $url, $matches)) {
$url = $matches[2] . $matches[3];
return $matches[1];
}
return '';
}
/**
* Breaks the request down into an object.
* @param string $raw Raw request.
* @return SimpleFormEncoding Parsed data.
* @access private
*/
function _parseRequest($raw) {
$this->_raw = $raw;
$request = new SimpleGetEncoding();
foreach (split("&", $raw) as $pair) {
if (preg_match('/(.*?)=(.*)/', $pair, $matches)) {
$request->add($matches[1], urldecode($matches[2]));
} elseif ($pair) {
$request->add($pair, '');
}
}
return $request;
}
/**
* Accessor for protocol part.
* @param string $default Value to use if not present.
* @return string Scheme name, e.g "http".
* @access public
*/
function getScheme($default = false) {
return $this->_scheme ? $this->_scheme : $default;
}
/**
* Accessor for user name.
* @return string Username preceding host.
* @access public
*/
function getUsername() {
return $this->_username;
}
/**
* Accessor for password.
* @return string Password preceding host.
* @access public
*/
function getPassword() {
return $this->_password;
}
/**
* Accessor for hostname and port.
* @param string $default Value to use if not present.
* @return string Hostname only.
* @access public
*/
function getHost($default = false) {
return $this->_host ? $this->_host : $default;
}
/**
* Accessor for top level domain.
* @return string Last part of host.
* @access public
*/
function getTld() {
$path_parts = pathinfo($this->getHost());
return (isset($path_parts['extension']) ? $path_parts['extension'] : false);
}
/**
* Accessor for port number.
* @return integer TCP/IP port number.
* @access public
*/
function getPort() {
return $this->_port;
}
/**
* Accessor for path.
* @return string Full path including leading slash if implied.
* @access public
*/
function getPath() {
if (! $this->_path && $this->_host) {
return '/';
}
return $this->_path;
}
/**
* Accessor for page if any. This may be a
* directory name if ambiguious.
* @return Page name.
* @access public
*/
function getPage() {
if (! preg_match('/([^\/]*?)$/', $this->getPath(), $matches)) {
return false;
}
return $matches[1];
}
/**
* Gets the path to the page.
* @return string Path less the page.
* @access public
*/
function getBasePath() {
if (! preg_match('/(.*\/)[^\/]*?$/', $this->getPath(), $matches)) {
return false;
}
return $matches[1];
}
/**
* Accessor for fragment at end of URL after the "#".
* @return string Part after "#".
* @access public
*/
function getFragment() {
return $this->_fragment;
}
/**
* Sets image coordinates. Set to false to clear
* them.
* @param integer $x Horizontal position.
* @param integer $y Vertical position.
* @access public
*/
function setCoordinates($x = false, $y = false) {
if (($x === false) || ($y === false)) {
$this->_x = $this->_y = false;
return;
}
$this->_x = (integer)$x;
$this->_y = (integer)$y;
}
/**
* Accessor for horizontal image coordinate.
* @return integer X value.
* @access public
*/
function getX() {
return $this->_x;
}
/**
* Accessor for vertical image coordinate.
* @return integer Y value.
* @access public
*/
function getY() {
return $this->_y;
}
/**
* Accessor for current request parameters
* in URL string form. Will return teh original request
* if at all possible even if it doesn't make much
* sense.
* @return string Form is string "?a=1&b=2", etc.
* @access public
*/
function getEncodedRequest() {
if ($this->_raw) {
$encoded = $this->_raw;
} else {
$encoded = $this->_request->asUrlRequest();
}
if ($encoded) {
return '?' . preg_replace('/^\?/', '', $encoded);
}
return '';
}
/**
* Adds an additional parameter to the request.
* @param string $key Name of parameter.
* @param string $value Value as string.
* @access public
*/
function addRequestParameter($key, $value) {
$this->_raw = false;
$this->_request->add($key, $value);
}
/**
* Adds additional parameters to the request.
* @param hash/SimpleFormEncoding $parameters Additional
* parameters.
* @access public
*/
function addRequestParameters($parameters) {
$this->_raw = false;
$this->_request->merge($parameters);
}
/**
* Clears down all parameters.
* @access public
*/
function clearRequest() {
$this->_raw = false;
$this->_request = &new SimpleGetEncoding();
}
/**
* Gets the frame target if present. Although
* not strictly part of the URL specification it
* acts as similarily to the browser.
* @return boolean/string Frame name or false if none.
* @access public
*/
function getTarget() {
return $this->_target;
}
/**
* Attaches a frame target.
* @param string $frame Name of frame.
* @access public
*/
function setTarget($frame) {
$this->_raw = false;
$this->_target = $frame;
}
/**
* Renders the URL back into a string.
* @return string URL in canonical form.
* @access public
*/
function asString() {
$path = $this->_path;
$scheme = $identity = $host = $encoded = $fragment = '';
if ($this->_username && $this->_password) {
$identity = $this->_username . ':' . $this->_password . '@';
}
if ($this->getHost()) {
$scheme = $this->getScheme() ? $this->getScheme() : 'http';
$scheme .= "://";
$host = $this->getHost();
}
if (substr($this->_path, 0, 1) == '/') {
$path = $this->normalisePath($this->_path);
}
$encoded = $this->getEncodedRequest();
$fragment = $this->getFragment() ? '#'. $this->getFragment() : '';
$coords = $this->getX() === false ? '' : '?' . $this->getX() . ',' . $this->getY();
return "$scheme$identity$host$path$encoded$fragment$coords";
}
/**
* Replaces unknown sections to turn a relative
* URL into an absolute one. The base URL can
* be either a string or a SimpleUrl object.
* @param string/SimpleUrl $base Base URL.
* @access public
*/
function makeAbsolute($base) {
if (! is_object($base)) {
$base = new SimpleUrl($base);
}
if ($this->getHost()) {
$scheme = $this->getScheme();
$host = $this->getHost();
$port = $this->getPort() ? ':' . $this->getPort() : '';
$identity = $this->getIdentity() ? $this->getIdentity() . '@' : '';
if (! $identity) {
$identity = $base->getIdentity() ? $base->getIdentity() . '@' : '';
}
} else {
$scheme = $base->getScheme();
$host = $base->getHost();
$port = $base->getPort() ? ':' . $base->getPort() : '';
$identity = $base->getIdentity() ? $base->getIdentity() . '@' : '';
}
$path = $this->normalisePath($this->_extractAbsolutePath($base));
$encoded = $this->getEncodedRequest();
$fragment = $this->getFragment() ? '#'. $this->getFragment() : '';
$coords = $this->getX() === false ? '' : '?' . $this->getX() . ',' . $this->getY();
return new SimpleUrl("$scheme://$identity$host$port$path$encoded$fragment$coords");
}
/**
* Replaces unknown sections of the path with base parts
* to return a complete absolute one.
* @param string/SimpleUrl $base Base URL.
* @param string Absolute path.
* @access private
*/
function _extractAbsolutePath($base) {
if ($this->getHost()) {
return $this->_path;
}
if (! $this->_isRelativePath($this->_path)) {
return $this->_path;
}
if ($this->_path) {
return $base->getBasePath() . $this->_path;
}
return $base->getPath();
}
/**
* Simple test to see if a path part is relative.
* @param string $path Path to test.
* @return boolean True if starts with a "/".
* @access private
*/
function _isRelativePath($path) {
return (substr($path, 0, 1) != '/');
}
/**
* Extracts the username and password for use in rendering
* a URL.
* @return string/boolean Form of username:password or false.
* @access public
*/
function getIdentity() {
if ($this->_username && $this->_password) {
return $this->_username . ':' . $this->_password;
}
return false;
}
/**
* Replaces . and .. sections of the path.
* @param string $path Unoptimised path.
* @return string Path with dots removed if possible.
* @access public
*/
function normalisePath($path) {
$path = preg_replace('|/\./|', '/', $path);
return preg_replace('|/[^/]+/\.\./|', '/', $path);
}
/**
* A pipe seperated list of all TLDs that result in two part
* domain names.
* @return string Pipe separated list.
* @access public
* @static
*/
function getAllTopLevelDomains() {
return 'com|edu|net|org|gov|mil|int|biz|info|name|pro|aero|coop|museum';
}
}
?>

162
thirdparty/spyc/README vendored Normal file
View File

@ -0,0 +1,162 @@
#
# S P Y C
# a simple php yaml class
# v0.2(.5)
#
# Load this README!
# >> $readme = Spyc::YAMLLoad('README');
#
--- %YAML:1.1
title: Spyc -- a Simple PHP YAML Class
version: 0.2.5
author: [chris wanstrath, chris@ozmm.org]
websites: [http://www.yaml.org, http://spyc.sourceforge.net]
license: [MIT License, http://www.opensource.org/licenses/mit-license.php]
copyright: (c) 2005-2006 Chris Wanstrath
tested on: [php 4.3.11, php 5.0.4]
installation: >
Copy spyc.php to a directory you can
access with your YAML-ready PHP script.
That's it!
about: >
From www.yaml.org:
"YAML(tm) (rhymes with 'camel') is a human-friendly, cross language,
Unicode based data serialization language designed around the common
native data structures of agile programming languages. It is broadly
useful for programming needs ranging from configuration files to
Internet messaging to object persistence to data auditing. Together
with the Unicode standard for characters, the YAML specification provides
all the information necessary to understand YAML Version 1.1 and to
creating programs that process YAML information.
YAML(tm) is a balance of the following design goals:
- YAML documents are very readable by humans.
- YAML interacts well with scripting languages.
- YAML uses host languages' native data structures.
- YAML has a consistent information model.
- YAML enables stream-based processing.
- YAML is expressive and extensible.
- YAML is easy to implement."
YAML makes a lot of sense. It's easy to use, easy to learn, and cool.
As the lucky stiff named why once said, "YAML is a beacon of light."
If you're new to YAML, may we suggest YAML In Five Minutes:
- http://yaml.kwiki.org/?YamlInFiveMinutes
If you don't have five minutes, realize that this README is a completely
valid YAML document. Dig in, load this or any YAML file into an array
with Spyc and see how easy it is to translate friendly text into usable
data.
The purpose of Spyc is to provide a pure PHP alternative to Syck, a
simple API for loading and dumping YAML documents, a YAML loader which
understands a usable subset of the YAML spec, and to further spread
the glory of YAML to the PHP masses.
If you're at all hesitant ("usable subset of YAML?!"), navigate
http://yaml.org/start.html. Spyc completely understands the YAML
document shown there, a document which has features way beyond the
scope of what normal config files might require. Try it for yourself,
and then start enjoying the peace of mind YAML brings to your life.
meat and a few potatoes:
- concept: Loading a YAML document into PHP
brief: >
$yaml will become an array of all the data in wicked.yaml
code: |
include('spyc.php');
$yaml = Spyc::YAMLLoad('wicked.yaml');
- concept: Loading a YAML string into PHP
brief: >
$array will look like this:
array('A YAML','document in a','string')
code: |
include('spyc.php');
$yaml = '- A YAML\n- document in a\n- string.';
$array = Spyc::YAMLLoad($yaml);
- concept: Dumping a PHP array to YAML
brief: >
$yaml will become a string of a YAML document created from
$array.
code: |
include('spyc.php');
$array['name'] = 'chris';
$array['sport'] = 'curbing';
$yaml = Spyc::YAMLDump($array);
prior art:
- who: [Brian Ingerson, Clark Evans, Oren Ben-Kiki]
why?: >
The YAML spec is really a piece of work, and these guys
did a great job on it. A simple and elegant language like
YAML was a long time coming and it's refreshing to know
such able minded individuals took the task to heart and
executed it with cunning and strength. In addition to
their various noteworthy contributions to YAML parsers
and related projects, YAML.pm's README is a treasure trove
of information for knowledge seekers. Thanks, guys.
- who: why the lucky stiff
why?: >
As the author of Syck, the code used in Ruby for the language's
YAML class and methods, why is indirectly (directly?) responsible
for my first exposure to YAML (as a config file in a Ruby web-app)
and the countless hours I spent playing with this sheik new data
format afterwards. Syck's README is a YAML file and thus the
inspiration for this file and, even, this very piece of software.
- who: Steve Howell
why?: >
Python's YAML implementation. PyYAML's README file is also YAML,
so it too inspired the YAML format of this README file.
- who: [Rasmus Lerdorf, Zeev Suraski, Andi Gutmans, et al]
why?: >
PHP is great at what it does best. It's also paid a lot of my bills.
Thanks.
bugs:
report: >
Please see Spyc's Sourceforge project page for information on reporting bugs.
speed: >
This implementation was not designed for speed. Rather, it
was designed for those who need a pure PHP implementation of
a YAML parser and who are not overly concerned with performance.
If you want speed, check out Syck.
depth: >
This parser is by no means a comprehensive YAML parser. For supported
features and future plans, check the website.
unicode: >
YAML is supposed to be unicode, but for now we're just using ASCII.
PHP has crappy unicode support but who knows what the future holds.
resources:
- http://www.yaml.org
- http://www.yaml.org/spec/
- http://yaml.kwiki.org/?YamlInFiveMinutes
- http://www.whytheluckystiff.net/syck/
- http://yaml4r.sourceforge.net/cookbook/
- http://www.sourceforge.net/projects/spyc/
- http://spyc.sourceforge.net/
thanks:
- Adam Wood
- Daniel Ferreira
- Aaron Jensen
- Mike Thornton
- Fabien Potencier
- Mustafa Kumas

868
thirdparty/spyc/Spyc.php vendored Normal file
View File

@ -0,0 +1,868 @@
<?php
/**
* Spyc -- A Simple PHP YAML Class
* @version 0.2.(5) -- 2006-12-31
* @author Chris Wanstrath <chris@ozmm.org>
* @author Vlad Andersen <vlad@oneiros.ru>
* @link http://spyc.sourceforge.net/
* @copyright Copyright 2005-2006 Chris Wanstrath
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* A node, used by Spyc for parsing YAML.
* @package sapphire
* @subpackage misc
*/
class YAMLNode {
/**#@+
* @access public
* @var string
*/
public $parent;
public $id;
/**#@-*/
/**
* @access public
* @var mixed
*/
public $data;
/**
* @access public
* @var int
*/
public $indent;
/**
* @access public
* @var bool
*/
public $children = false;
/**
* The constructor assigns the node a unique ID.
* @access public
* @return void
*/
public function YAMLNode($nodeId) {
$this->id = $nodeId;
}
}
/**
* The Simple PHP YAML Class.
*
* This class can be used to read a YAML file and convert its contents
* into a PHP array. It currently supports a very limited subsection of
* the YAML spec.
*
* Usage:
* <code>
* $parser = new Spyc;
* $array = $parser->load($file);
* </code>
* @package sapphire
* @subpackage misc
*/
class Spyc {
/**
* Load YAML into a PHP array statically
*
* The load method, when supplied with a YAML stream (string or file),
* will do its best to convert YAML in a file into a PHP array. Pretty
* simple.
* Usage:
* <code>
* $array = Spyc::YAMLLoad('lucky.yaml');
* print_r($array);
* </code>
* @access public
* @return array
* @param string $input Path of YAML file or string containing YAML
*/
public static function YAMLLoad($input) {
$spyc = new Spyc;
return $spyc->load($input);
}
/**
* Dump YAML from PHP array statically
*
* The dump method, when supplied with an array, will do its best
* to convert the array into friendly YAML. Pretty simple. Feel free to
* save the returned string as nothing.yaml and pass it around.
*
* Oh, and you can decide how big the indent is and what the wordwrap
* for folding is. Pretty cool -- just pass in 'false' for either if
* you want to use the default.
*
* Indent's default is 2 spaces, wordwrap's default is 40 characters. And
* you can turn off wordwrap by passing in 0.
*
* @access public
* @return string
* @param array $array PHP array
* @param int $indent Pass in false to use the default, which is 2
* @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
*/
public static function YAMLDump($array,$indent = false,$wordwrap = false) {
$spyc = new Spyc;
return $spyc->dump($array,$indent,$wordwrap);
}
/**
* Load YAML into a PHP array from an instantiated object
*
* The load method, when supplied with a YAML stream (string or file path),
* will do its best to convert the YAML into a PHP array. Pretty simple.
* Usage:
* <code>
* $parser = new Spyc;
* $array = $parser->load('lucky.yaml');
* print_r($array);
* </code>
* @access public
* @return array
* @param string $input Path of YAML file or string containing YAML
*/
public function load($input) {
// See what type of input we're talking about
// If it's not a file, assume it's a string
if (!empty($input) && (strpos($input, "\n") === false)
&& file_exists($input)) {
$yaml = file($input);
} else {
$yaml = explode("\n",$input);
}
// Initiate some objects and values
$base = new YAMLNode (1);
$base->indent = 0;
$this->_lastIndent = 0;
$this->_lastNode = $base->id;
$this->_inBlock = false;
$this->_isInline = false;
$this->_nodeId = 2;
foreach ($yaml as $linenum => $line) {
$ifchk = trim($line);
// If the line starts with a tab (instead of a space), throw a fit.
if (preg_match('/^(\t)+(\w+)/', $line)) {
$err = 'ERROR: Line '. ($linenum + 1) .' in your input YAML begins'.
' with a tab. YAML only recognizes spaces. Please reformat.';
die($err);
}
if ($this->_inBlock === false && empty($ifchk)) {
continue;
} elseif ($this->_inBlock == true && empty($ifchk)) {
$last =& $this->_allNodes[$this->_lastNode];
$last->data[key($last->data)] .= "\n";
} elseif ($ifchk{0} != '#' && substr($ifchk,0,3) != '---') {
// Create a new node and get its indent
$node = new YAMLNode ($this->_nodeId);
$this->_nodeId++;
$node->indent = $this->_getIndent($line);
// Check where the node lies in the hierarchy
if ($this->_lastIndent == $node->indent) {
// If we're in a block, add the text to the parent's data
if ($this->_inBlock === true) {
$parent =& $this->_allNodes[$this->_lastNode];
$parent->data[key($parent->data)] .= trim($line).$this->_blockEnd;
} else {
// The current node's parent is the same as the previous node's
if (isset($this->_allNodes[$this->_lastNode])) {
$node->parent = $this->_allNodes[$this->_lastNode]->parent;
}
}
} elseif ($this->_lastIndent < $node->indent) {
if ($this->_inBlock === true) {
$parent =& $this->_allNodes[$this->_lastNode];
$parent->data[key($parent->data)] .= trim($line).$this->_blockEnd;
} elseif ($this->_inBlock === false) {
// The current node's parent is the previous node
$node->parent = $this->_lastNode;
// If the value of the last node's data was > or | we need to
// start blocking i.e. taking in all lines as a text value until
// we drop our indent.
$parent =& $this->_allNodes[$node->parent];
$this->_allNodes[$node->parent]->children = true;
if (is_array($parent->data)) {
// if (isset ($parent->data[key($parent->data)]))
$chk = $parent->data[key($parent->data)];
if ($chk === '>') {
$this->_inBlock = true;
$this->_blockEnd = ' ';
$parent->data[key($parent->data)] =
str_replace('>','',$parent->data[key($parent->data)]);
$parent->data[key($parent->data)] .= trim($line).' ';
$this->_allNodes[$node->parent]->children = false;
$this->_lastIndent = $node->indent;
} elseif ($chk === '|') {
$this->_inBlock = true;
$this->_blockEnd = "\n";
$parent->data[key($parent->data)] =
str_replace('|','',$parent->data[key($parent->data)]);
$parent->data[key($parent->data)] .= trim($line)."\n";
$this->_allNodes[$node->parent]->children = false;
$this->_lastIndent = $node->indent;
}
}
}
} elseif ($this->_lastIndent > $node->indent) {
// Any block we had going is dead now
if ($this->_inBlock === true) {
$this->_inBlock = false;
if ($this->_blockEnd = "\n") {
$last =& $this->_allNodes[$this->_lastNode];
$last->data[key($last->data)] =
trim($last->data[key($last->data)]);
}
}
// We don't know the parent of the node so we have to find it
// foreach ($this->_allNodes as $n) {
foreach ($this->_indentSort[$node->indent] as $n) {
if ($n->indent == $node->indent) {
$node->parent = $n->parent;
}
}
}
if ($this->_inBlock === false) {
// Set these properties with information from our current node
$this->_lastIndent = $node->indent;
// Set the last node
$this->_lastNode = $node->id;
// Parse the YAML line and return its data
$node->data = $this->_parseLine($line);
// Add the node to the master list
$this->_allNodes[$node->id] = $node;
// Add a reference to the parent list
$this->_allParent[intval($node->parent)][] = $node->id;
// Add a reference to the node in an indent array
$this->_indentSort[$node->indent][] =& $this->_allNodes[$node->id];
// Add a reference to the node in a References array if this node
// has a YAML reference in it.
if (
( (is_array($node->data)) &&
isset($node->data[key($node->data)]) &&
(!is_array($node->data[key($node->data)])) )
&&
( (preg_match('/^&([^ ]+)/',$node->data[key($node->data)]))
||
(preg_match('/^\*([^ ]+)/',$node->data[key($node->data)])) )
) {
$this->_haveRefs[] =& $this->_allNodes[$node->id];
} elseif (
( (is_array($node->data)) &&
isset($node->data[key($node->data)]) &&
(is_array($node->data[key($node->data)])) )
) {
// Incomplete reference making code. Ugly, needs cleaned up.
foreach ($node->data[key($node->data)] as $d) {
if ( !is_array($d) &&
( (preg_match('/^&([^ ]+)/',$d))
||
(preg_match('/^\*([^ ]+)/',$d)) )
) {
$this->_haveRefs[] =& $this->_allNodes[$node->id];
}
}
}
}
}
}
unset($node);
// Here we travel through node-space and pick out references (& and *)
$this->_linkReferences();
// Build the PHP array out of node-space
$trunk = $this->_buildArray();
return $trunk;
}
/**
* Dump PHP array to YAML
*
* The dump method, when supplied with an array, will do its best
* to convert the array into friendly YAML. Pretty simple. Feel free to
* save the returned string as tasteful.yaml and pass it around.
*
* Oh, and you can decide how big the indent is and what the wordwrap
* for folding is. Pretty cool -- just pass in 'false' for either if
* you want to use the default.
*
* Indent's default is 2 spaces, wordwrap's default is 40 characters. And
* you can turn off wordwrap by passing in 0.
*
* @access public
* @return string
* @param array $array PHP array
* @param int $indent Pass in false to use the default, which is 2
* @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
*/
public function dump($array,$indent = false,$wordwrap = false) {
// Dumps to some very clean YAML. We'll have to add some more features
// and options soon. And better support for folding.
// New features and options.
if ($indent === false or !is_numeric($indent)) {
$this->_dumpIndent = 2;
} else {
$this->_dumpIndent = $indent;
}
if ($wordwrap === false or !is_numeric($wordwrap)) {
$this->_dumpWordWrap = 40;
} else {
$this->_dumpWordWrap = $wordwrap;
}
// New YAML document
$string = "---\n";
// Start at the base of the array and move through it.
foreach ($array as $key => $value) {
$string .= $this->_yamlize($key,$value,0);
}
return $string;
}
/**** Private Properties ****/
/**#@+
* @access private
* @var mixed
*/
private $_haveRefs;
private $_allNodes;
private $_allParent;
private $_lastIndent;
private $_lastNode;
private $_inBlock;
private $_isInline;
private $_dumpIndent;
private $_dumpWordWrap;
/**#@-*/
/**** Public Properties ****/
/**#@+
* @access public
* @var mixed
*/
public $_nodeId;
/**#@-*/
/**** Private Methods ****/
/**
* Attempts to convert a key / value array item to YAML
* @access private
* @return string
* @param $key The name of the key
* @param $value The value of the item
* @param $indent The indent of the current node
*/
private function _yamlize($key,$value,$indent) {
if (is_array($value)) {
// It has children. What to do?
// Make it the right kind of item
$string = $this->_dumpNode($key,NULL,$indent);
// Add the indent
$indent += $this->_dumpIndent;
// Yamlize the array
$string .= $this->_yamlizeArray($value,$indent);
} elseif (!is_array($value)) {
// It doesn't have children. Yip.
$string = $this->_dumpNode($key,$value,$indent);
}
return $string;
}
/**
* Attempts to convert an array to YAML
* @access private
* @return string
* @param $array The array you want to convert
* @param $indent The indent of the current level
*/
private function _yamlizeArray($array,$indent) {
if (is_array($array)) {
$string = '';
foreach ($array as $key => $value) {
$string .= $this->_yamlize($key,$value,$indent);
}
return $string;
} else {
return false;
}
}
/**
* Returns YAML from a key and a value
* @access private
* @return string
* @param $key The name of the key
* @param $value The value of the item
* @param $indent The indent of the current node
*/
private function _dumpNode($key,$value,$indent) {
// do some folding here, for blocks
if (strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false) {
$value = $this->_doLiteralBlock($value,$indent);
} else {
$value = $this->_doFolding($value,$indent);
}
if (is_bool($value)) {
$value = ($value) ? "true" : "false";
}
$spaces = str_repeat(' ',$indent);
if (is_int($key)) {
// It's a sequence
$string = $spaces.'- '.$value."\n";
} else {
// It's mapped
$string = $spaces.$key.': '.$value."\n";
}
return $string;
}
/**
* Creates a literal block for dumping
* @access private
* @return string
* @param $value
* @param $indent int The value of the indent
*/
private function _doLiteralBlock($value,$indent) {
$exploded = explode("\n",$value);
$newValue = '|';
$indent += $this->_dumpIndent;
$spaces = str_repeat(' ',$indent);
foreach ($exploded as $line) {
$newValue .= "\n" . $spaces . trim($line);
}
return $newValue;
}
/**
* Folds a string of text, if necessary
* @access private
* @return string
* @param $value The string you wish to fold
*/
private function _doFolding($value,$indent) {
// Don't do anything if wordwrap is set to 0
if ($this->_dumpWordWrap === 0) {
return $value;
}
if (strlen($value) > $this->_dumpWordWrap) {
$indent += $this->_dumpIndent;
$indent = str_repeat(' ',$indent);
$wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
$value = ">\n".$indent.$wrapped;
}
return $value;
}
/* Methods used in loading */
/**
* Finds and returns the indentation of a YAML line
* @access private
* @return int
* @param string $line A line from the YAML file
*/
private function _getIndent($line) {
preg_match('/^\s{1,}/',$line,$match);
if (!empty($match[0])) {
$indent = substr_count($match[0],' ');
} else {
$indent = 0;
}
return $indent;
}
/**
* Parses YAML code and returns an array for a node
* @access private
* @return array
* @param string $line A line from the YAML file
*/
private function _parseLine($line) {
$line = trim($line);
$array = array();
if (preg_match('/^-(.*):$/',$line)) {
// It's a mapped sequence
$key = trim(substr(substr($line,1),0,-1));
$array[$key] = '';
} elseif ($line[0] == '-' && substr($line,0,3) != '---') {
// It's a list item but not a new stream
if (strlen($line) > 1) {
$value = trim(substr($line,1));
// Set the type of the value. Int, string, etc
$value = $this->_toType($value);
$array[] = $value;
} else {
$array[] = array();
}
} elseif (preg_match('/^(.+):/',$line,$key)) {
// It's a key/value pair most likely
// If the key is in double quotes pull it out
if (preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
$value = trim(str_replace($matches[1],'',$line));
$key = $matches[2];
} else {
// Do some guesswork as to the key and the value
$explode = explode(':',$line);
$key = trim($explode[0]);
array_shift($explode);
$value = trim(implode(':',$explode));
}
// Set the type of the value. Int, string, etc
$value = $this->_toType($value);
if (empty($key)) {
$array[] = $value;
} else {
$array[$key] = $value;
}
}
return $array;
}
/**
* Finds the type of the passed value, returns the value as the new type.
* @access private
* @param string $value
* @return mixed
*/
private function _toType($value) {
if (preg_match('/^("(.*)"|\'(.*)\')/',$value,$matches)) {
$value = (string)preg_replace('/(\'\'|\\\\\')/',"'",end($matches));
$value = preg_replace('/\\\\"/','"',$value);
} elseif (preg_match('/^\\[(.+)\\]$/',$value,$matches)) {
// Inline Sequence
// Take out strings sequences and mappings
$explode = $this->_inlineEscape($matches[1]);
// Propogate value array
$value = array();
foreach ($explode as $v) {
$value[] = $this->_toType($v);
}
} elseif (strpos($value,': ')!==false && !preg_match('/^{(.+)/',$value)) {
// It's a map
$array = explode(': ',$value);
$key = trim($array[0]);
array_shift($array);
$value = trim(implode(': ',$array));
$value = $this->_toType($value);
$value = array($key => $value);
} elseif (preg_match("/{(.+)}$/",$value,$matches)) {
// Inline Mapping
// Take out strings sequences and mappings
$explode = $this->_inlineEscape($matches[1]);
// Propogate value array
$array = array();
foreach ($explode as $v) {
$array = $array + $this->_toType($v);
}
$value = $array;
} elseif (strtolower($value) == 'null' or $value == '' or $value == '~') {
$value = NULL;
} elseif (preg_match ('/^[0-9]+$/', $value)) {
$value = (int)$value;
} elseif (in_array(strtolower($value),
array('true', 'on', '+', 'yes', 'y'))) {
$value = TRUE;
} elseif (in_array(strtolower($value),
array('false', 'off', '-', 'no', 'n'))) {
$value = FALSE;
} elseif (is_numeric($value)) {
$value = (float)$value;
} else {
// Just a normal string, right?
$value = trim(preg_replace('/#(.+)$/','',$value));
}
return $value;
}
/**
* Used in inlines to check for more inlines or quoted strings
* @access private
* @return array
*/
private function _inlineEscape($inline) {
// There's gotta be a cleaner way to do this...
// While pure sequences seem to be nesting just fine,
// pure mappings and mappings with sequences inside can't go very
// deep. This needs to be fixed.
$saved_strings = array();
// Check for strings
$regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
if (preg_match_all($regex,$inline,$strings)) {
$saved_strings = $strings[0];
$inline = preg_replace($regex,'YAMLString',$inline);
}
unset($regex);
// Check for sequences
if (preg_match_all('/\[(.+)\]/U',$inline,$seqs)) {
$inline = preg_replace('/\[(.+)\]/U','YAMLSeq',$inline);
$seqs = $seqs[0];
}
// Check for mappings
if (preg_match_all('/{(.+)}/U',$inline,$maps)) {
$inline = preg_replace('/{(.+)}/U','YAMLMap',$inline);
$maps = $maps[0];
}
$explode = explode(', ',$inline);
// Re-add the sequences
if (!empty($seqs)) {
$i = 0;
foreach ($explode as $key => $value) {
if (strpos($value,'YAMLSeq') !== false) {
$explode[$key] = str_replace('YAMLSeq',$seqs[$i],$value);
++$i;
}
}
}
// Re-add the mappings
if (!empty($maps)) {
$i = 0;
foreach ($explode as $key => $value) {
if (strpos($value,'YAMLMap') !== false) {
$explode[$key] = str_replace('YAMLMap',$maps[$i],$value);
++$i;
}
}
}
// Re-add the strings
if (!empty($saved_strings)) {
$i = 0;
foreach ($explode as $key => $value) {
while (strpos($value,'YAMLString') !== false) {
$explode[$key] = preg_replace('/YAMLString/',$saved_strings[$i],$value, 1);
++$i;
$value = $explode[$key];
}
}
}
return $explode;
}
/**
* Builds the PHP array from all the YAML nodes we've gathered
* @access private
* @return array
*/
private function _buildArray() {
$trunk = array();
if (!isset($this->_indentSort[0])) {
return $trunk;
}
foreach ($this->_indentSort[0] as $n) {
if (empty($n->parent)) {
$this->_nodeArrayizeData($n);
// Check for references and copy the needed data to complete them.
$this->_makeReferences($n);
// Merge our data with the big array we're building
$trunk = $this->_array_kmerge($trunk,$n->data);
}
}
return $trunk;
}
/**
* Traverses node-space and sets references (& and *) accordingly
* @access private
* @return bool
*/
private function _linkReferences() {
if (is_array($this->_haveRefs)) {
foreach ($this->_haveRefs as $node) {
if (!empty($node->data)) {
$key = key($node->data);
// If it's an array, don't check.
if (is_array($node->data[$key])) {
foreach ($node->data[$key] as $k => $v) {
$this->_linkRef($node,$key,$k,$v);
}
} else {
$this->_linkRef($node,$key);
}
}
}
}
return true;
}
function _linkRef(&$n,$key,$k = NULL,$v = NULL) {
if (empty($k) && empty($v)) {
// Look for &refs
if (preg_match('/^&([^ ]+)/',$n->data[$key],$matches)) {
// Flag the node so we know it's a reference
$this->_allNodes[$n->id]->ref = substr($matches[0],1);
$this->_allNodes[$n->id]->data[$key] =
substr($n->data[$key],strlen($matches[0])+1);
// Look for *refs
} elseif (preg_match('/^\*([^ ]+)/',$n->data[$key],$matches)) {
$ref = substr($matches[0],1);
// Flag the node as having a reference
$this->_allNodes[$n->id]->refKey = $ref;
}
} elseif (!empty($k) && !empty($v)) {
if (preg_match('/^&([^ ]+)/',$v,$matches)) {
// Flag the node so we know it's a reference
$this->_allNodes[$n->id]->ref = substr($matches[0],1);
$this->_allNodes[$n->id]->data[$key][$k] =
substr($v,strlen($matches[0])+1);
// Look for *refs
} elseif (preg_match('/^\*([^ ]+)/',$v,$matches)) {
$ref = substr($matches[0],1);
// Flag the node as having a reference
$this->_allNodes[$n->id]->refKey = $ref;
}
}
}
/**
* Finds the children of a node and aids in the building of the PHP array
* @access private
* @param int $nid The id of the node whose children we're gathering
* @return array
*/
private function _gatherChildren($nid) {
$return = array();
$node =& $this->_allNodes[$nid];
if (is_array ($this->_allParent[$node->id])) {
foreach ($this->_allParent[$node->id] as $nodeZ) {
$z =& $this->_allNodes[$nodeZ];
// We found a child
$this->_nodeArrayizeData($z);
// Check for references
$this->_makeReferences($z);
// Merge with the big array we're returning
// The big array being all the data of the children of our parent node
$return = $this->_array_kmerge($return,$z->data);
}
}
return $return;
}
/**
* Turns a node's data and its children's data into a PHP array
*
* @access private
* @param array $node The node which you want to arrayize
* @return boolean
*/
private function _nodeArrayizeData(&$node) {
if (is_array($node->data) && $node->children == true) {
// This node has children, so we need to find them
$childs = $this->_gatherChildren($node->id);
// We've gathered all our children's data and are ready to use it
$key = key($node->data);
$key = empty($key) ? 0 : $key;
// If it's an array, add to it of course
if (isset ($node->data[$key])) {
if (is_array($node->data[$key])) {
$node->data[$key] = $this->_array_kmerge($node->data[$key],$childs);
} else {
$node->data[$key] = $childs;
}
} else {
$node->data[$key] = $childs;
}
} elseif (!is_array($node->data) && $node->children == true) {
// Same as above, find the children of this node
$childs = $this->_gatherChildren($node->id);
$node->data = array();
$node->data[] = $childs;
}
// We edited $node by reference, so just return true
return true;
}
/**
* Traverses node-space and copies references to / from this object.
* @access private
* @param object $z A node whose references we wish to make real
* @return bool
*/
private function _makeReferences(&$z) {
// It is a reference
if (isset($z->ref)) {
$key = key($z->data);
// Copy the data to this object for easy retrieval later
$this->ref[$z->ref] =& $z->data[$key];
// It has a reference
} elseif (isset($z->refKey)) {
if (isset($this->ref[$z->refKey])) {
$key = key($z->data);
// Copy the data from this object to make the node a real reference
$z->data[$key] =& $this->ref[$z->refKey];
}
}
return true;
}
/**
* Merges arrays and maintains numeric keys.
*
* An ever-so-slightly modified version of the array_kmerge() function posted
* to php.net by mail at nospam dot iaindooley dot com on 2004-04-08.
*
* http://us3.php.net/manual/en/function.array-merge.php
*
* @access private
* @param array $arr1
* @param array $arr2
* @return array
*/
private function _array_kmerge($arr1,$arr2) {
if(!is_array($arr1)) $arr1 = array();
if(!is_array($arr2)) $arr2 = array();
$keys = array_merge(array_keys($arr1),array_keys($arr2));
$vals = array_merge(array_values($arr1),array_values($arr2));
$ret = array();
foreach($keys as $key) {
list($unused,$val) = each($vals);
if (isset($ret[$key]) and is_int($key)) $ret[] = $val; else $ret[$key] = $val;
}
return $ret;
}
}
?>