commit e62663cae7ad01a56072c01a4fdd7de7a421ca61 Author: Jeff Welch Date: Mon Feb 4 23:07:53 2013 -0500 It begins. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b44f495 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +phpunit.xml +composer.lock +composer.phar +vendor/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9957d50 --- /dev/null +++ b/LICENSE @@ -0,0 +1,33 @@ +PHPExporter + +Copyright (c) 2002-2013, Sebastian Bergmann . +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 Sebastian Bergmann nor the names of his + 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 OWNER OR 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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..34aa1f7 --- /dev/null +++ b/README.md @@ -0,0 +1,198 @@ +PHPExporter +=========== + +PHPExporter is a utility for getting textual representations of PHP values. + +```php + Array &1 ( + 0 => 1 + 1 => 2 + 2 => 3 + ) + 1 => Array &2 ( + 0 => '' + 1 => 0 + 2 => false + ) +) +*/ +echo Exporter::export(array(array(1,2,3), array("",0,FALSE))); + + +// Array with references +// ---------------------- +$array = array(); +$array['self'] = &$array; +echo Exporter::export($array); +/* +Array &0 ( + 'self' => Array &1 ( + 'self' => Array &1 + ) +) +*/ + + +// Object with references +// ---------------------- +$obj = new stdClass(); +$obj->self = $obj; +echo Exporter::export($obj); +/* +stdClass Object &0000000003a66dcc0000000025e723e2 ( + 'self' => stdClass Object &0000000003a66dcc0000000025e723e2 +) +*/ + + +// Kitchen Sink +// ------------ +$obj2 = new stdClass; +$obj2->foo = 'bar'; +$obj = new stdClass; +$obj->null = NULL; +$obj->boolean = TRUE; +$obj->integer = 1; +$obj->double = 1.2; +$obj->string = '1'; +$obj->text = "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext"; +$obj->object = $obj2; +$obj->objectagain = $obj2; +$obj->array = array('foo' => 'bar'); +$obj->self = $obj; +$array = array( + 0 => 0, + 'null' => NULL, + 'boolean' => TRUE, + 'integer' => 1, + 'double' => 1.2, + 'string' => '1', + 'text' => "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", + 'object' => $obj2, + 'objectagain' => $obj2, + 'array' => array('foo' => 'bar'), +); +$array['self'] = &$array; +echo Exporter::export($array); +/* +Array &0 ( + 0 => 0 + 'null' => null + 'boolean' => true + 'integer' => 1 + 'double' => 1.2 + 'string' => '1' + 'text' => 'this +is +a +very +very +very +very +very +very +long +text' + 'object' => stdClass Object &000000007ed49b01000000006907e890 ( + 'foo' => 'bar' + ) + 'objectagain' => stdClass Object &000000007ed49b01000000006907e890 + 'array' => Array &1 ( + 'foo' => 'bar' + ) + 'self' => Array &2 ( + 0 => 0 + 'null' => null + 'boolean' => true + 'integer' => 1 + 'double' => 1.2 + 'string' => '1' + 'text' => 'this +is +a +very +very +very +very +very +very +long +text' + 'object' => stdClass Object &000000007ed49b01000000006907e890 + 'objectagain' => stdClass Object &000000007ed49b01000000006907e890 + 'array' => Array &3 ( + 'foo' => 'bar' + ) + 'self' => Array &2 + ) +) +*/ + +``` + +PHPExporter works with PHP 5.3.3 or later. + +## Installation + +The recommended way to install PHPExporter is [through +composer](http://getcomposer.org). Just create a `composer.json` file and +run the `php composer.phar install` command to install it: + + { + "require": { + "phpexporter/phpexporter": "1.0.*@dev" + } + } + +## Tests + +To run the test suite, you need [composer](http://getcomposer.org). + + $ php composer.phar install --dev + $ phpunit + +## Acknowledgements + +This utility was adapted from the +[PHPUnit](https://github.com/sebastianbergmann/phpunit/) project. A special +thanks goes to the following people for their contributions: + + * [sebastianbergmann](https://github.com/sebastianbergmann) + * [edorian](https://github.com/edorian) + * [LawnGnome](https://github.com/LawnGnome) + * [bschussek](https://github.com/bschussek) + +## License + +PHPExporter is licensed under the BSD 3-Clause license. \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..b6c3de9 --- /dev/null +++ b/composer.json @@ -0,0 +1,43 @@ +{ + "name": "phpexporter/phpexporter", + "description": "Textual representations of PHP values (Adapted from PHPUnit_Util_Type)", + "keywords": ["exporter"], + "homepage": "http://www.github.com/whatthejeff/PHPExporter", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "autoload": { + "psr-0": { "PHPExporter": "src/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..d723527 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + + ./tests/PHPExporter/ + + + + + ./src + + + \ No newline at end of file diff --git a/src/PHPExporter/Exporter.php b/src/PHPExporter/Exporter.php new file mode 100644 index 0000000..86e59f3 --- /dev/null +++ b/src/PHPExporter/Exporter.php @@ -0,0 +1,277 @@ +. + * 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 Sebastian Bergmann nor the names of his + * 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 OWNER OR 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. + * + * @package PHPExporter + * @author Sebastian Bergmann + * @copyright 2001-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/whatthejeff/PHPExporter + */ + +namespace PHPExporter; + +/** + * Textual representations of PHP values (Adapted from PHPUnit_Util_Type). + * + * @package PHPExporter + * @author Sebastian Bergmann + * @copyright 2001-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/whatthejeff/PHPExporter + */ +class Exporter +{ + /** + * Exports a value into a string + * + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: + * + * - NULL is rendered as "null" (instead of "") + * - TRUE is rendered as "true" (instead of "1") + * - FALSE is rendered as "false" (instead of "") + * - Strings are always quoted with single quotes + * - Carriage returns and newlines are normalized to \n + * - Recursion and repeated rendering is treated properly + * + * @param mixed $value The value to export + * @param integer $indentation The indentation level of the 2nd+ line + * @return string + */ + public static function export($value, $indentation = 0) + { + return self::recursiveExport($value, $indentation); + } + + /** + * Recursive implementation of export + * + * @param mixed $value The value to export + * @param integer $indentation The indentation level of the 2nd+ line + * @param PHPExporter\ExporterContext $processed Contains all objects + * and arrays that have + * previously been + * rendered + * @return string + * @see PHPExporter\Exporter::export + */ + protected static function recursiveExport(&$value, $indentation, $processed = null) + { + if ($value === NULL) { + return 'null'; + } + + if ($value === TRUE) { + return 'true'; + } + + if ($value === FALSE) { + return 'false'; + } + + if (is_float($value) && floatval(intval($value)) === $value) { + return "$value.0"; + } + + if (is_resource($value)) { + return sprintf( + 'resource(%d) of type (%s)', + $value, + get_resource_type($value) + ); + } + + if (is_string($value)) { + // Match for most non printable chars somewhat taking multibyte chars into account + if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { + return 'Binary String: 0x' . bin2hex($value); + } + + return "'" . + str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . + "'"; + } + + $whitespace = str_repeat(' ', 4 * $indentation); + + if (!$processed) { + $processed = new ExporterContext; + } + + if (is_array($value)) { + if (($key = $processed->contains($value)) !== false) { + return "Array &$key"; + } + + $key = $processed->add($value); + if (count($value) > 0) { + $output = "Array &$key (\n"; + + foreach ($value as $k => $v) { + $ek = self::export($k); + $output .= "$whitespace $ek => ".self::recursiveExport($value[$k], $indentation + 1, $processed)."\n"; + } + + return "$output$whitespace)"; + } else { + return "Array &$key ()"; + } + } + + if (is_object($value)) { + $class = get_class($value); + + if ($hash = $processed->contains($value)) { + return "$class Object &$hash"; + } + + $hash = $processed->add($value); + $array = self::toArray($value); + if (count($array) > 0) { + $output = "$class Object &$hash (\n"; + + foreach ($array as $k => $v) { + $k = self::export($k); + $output .= "$whitespace $k => ".self::recursiveExport($v, $indentation + 1, $processed)."\n"; + } + + return "$output$whitespace)"; + } else { + return "$class Object &$hash ()"; + } + + } + + return var_export($value, true); + } + + /** + * Exports a value into a single-line string + * + * The output of this method is similar to the output of + * PHPExporter\Exporter::export. This method guarantees thought that the + * result contains now newlines. + * + * Newlines are replaced by the visible string '\n'. Contents of arrays + * and objects (if any) are replaced by '...'. + * + * @param mixed $value The value to export + * @param integer $indentation The indentation level of the 2nd+ line + * @return string + * @see PHPExporter\Exporter::export + */ + public static function shortenedExport($value) + { + if (is_string($value)) { + return self::shortenedString($value); + } + + if(is_object($value)) { + return sprintf( + "%s Object (%s)", + get_class($value), + count(self::toArray($value)) > 0 ? '...' : '' + ); + } + + if (is_array($value)) { + return sprintf( + "Array (%s)", + count($value) > 0 ? '...' : '' + ); + } + + return self::export($value); + } + + /** + * Shortens a string and converts all new lines to '\n' + * + * @param string $string The string to shorten + * @param integer $max The maximum length for the string + * @return string + */ + public static function shortenedString($string, $maxLength = 40) + { + $string = self::export($string); + + if (strlen($string) > $maxLength) { + $string = substr($string, 0, $maxLength - 10) . '...' . substr($string, -7); + } + + return str_replace("\n", '\n', $string); + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + public static function toArray($object) + { + $array = array(); + + foreach ((array)$object as $key => $value) { + // properties are transformed to keys in the following way: + + // private $property => "\0Classname\0property" + // protected $property => "\0*\0property" + // public $property => "property" + + if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { + $key = $matches[1]; + } + + $array[$key] = $value; + } + + // Some internal classes like SplObjectStorage don't work with the + // above (fast) mechanism nor with reflection + // Format the output similarly to print_r() in this case + if ($object instanceof \SplObjectStorage) { + foreach ($object as $key => $value) { + $array[spl_object_hash($value)] = array( + 'obj' => $value, + 'inf' => $object->getInfo(), + ); + } + } + + return $array; + } +} diff --git a/src/PHPExporter/ExporterContext.php b/src/PHPExporter/ExporterContext.php new file mode 100644 index 0000000..a718846 --- /dev/null +++ b/src/PHPExporter/ExporterContext.php @@ -0,0 +1,187 @@ +. + * 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 Sebastian Bergmann nor the names of his + * 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 OWNER OR 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. + * + * @package PHPExporter + * @author Adam Harvey + * @copyright 2001-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/whatthejeff/PHPExporter + */ + +namespace PHPExporter; + +/** + * A context containing previously rendered arrays and objects when recursively + * exporting a value. + * + * @package PHPExporter + * @author Adam Harvey + * @copyright 2001-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/whatthejeff/PHPExporter + */ +class ExporterContext { + /** + * Previously seen arrays. + * + * @var array[] $arrays + */ + protected $arrays; + + /** + * Previously seen objects. + * + * @var SplObjectStorage $objects + */ + protected $objects; + + /** Initialises the context. */ + public function __construct() + { + $this->arrays = array(); + $this->objects = new \SplObjectStorage; + } + + /** + * Adds a value to the export context. + * + * @param mixed $value The value to add. + * @return mixed The ID of the stored value, either as a string or integer. + * @throws PHPExporter\ExporterContext Thrown if $value is not an array or + * object. + */ + public function add(&$value) + { + if (is_array($value)) { + return $this->addArray($value); + } elseif (is_object($value)) { + return $this->addObject($value); + } + + throw new ExporterException('Only arrays and objects are supported'); + } + + /** + * Checks if the given value exists within the context. + * + * @param mixed $value The value to check. + * @return mixed The string or integer ID of the stored value if it has + * already been seen, or boolean false if the value is not + * stored. + * @throws PHPExporter\ExporterContext Thrown if $value is not an array or + * object. + */ + public function contains(&$value) + { + if (is_array($value)) { + return $this->containsArray($value); + } elseif (is_object($value)) { + return $this->containsObject($value); + } + + throw new ExporterException('Only arrays and objects are supported'); + } + + /** + * Stores an array within the context. + * + * @param array $value The value to store. + * @return integer The internal ID of the array. + */ + protected function addArray(array &$value) + { + if (($key = $this->containsArray($value)) !== false) { + return $key; + } + + $this->arrays[] = &$value; + return count($this->arrays) - 1; + } + + /** + * Stores an object within the context. + * + * @param object $value + * @return string The ID of the object. + */ + protected function addObject($value) + { + if (!$this->objects->contains($value)) { + $this->objects->attach($value); + } + + return spl_object_hash($value); + } + + /** + * Checks if the given array exists within the context. + * + * @param array $value The array to check. + * @return mixed The integer ID of the array if it exists, or boolean false + * otherwise. + */ + protected function containsArray(array &$value) + { + $keys = array_keys($this->arrays, $value, true); + $gen = '_PHPExporter_Key_'.hash('sha512', microtime(true)); + foreach ($keys as $key) { + $this->arrays[$key][$gen] = $gen; + if (isset($value[$gen]) && $value[$gen] === $gen) { + unset($this->arrays[$key][$gen]); + return $key; + } + unset($this->arrays[$key][$gen]); + } + + return false; + } + + /** + * Checks if the given object exists within the context. + * + * @param object $value The object to check. + * @return mixed The string ID of the object if it exists, or boolean false + * otherwise. + */ + protected function containsObject($value) + { + if ($this->objects->contains($value)) { + return spl_object_hash($value); + } + + return false; + } +} diff --git a/src/PHPExporter/ExporterException.php b/src/PHPExporter/ExporterException.php new file mode 100644 index 0000000..bec86fc --- /dev/null +++ b/src/PHPExporter/ExporterException.php @@ -0,0 +1,58 @@ +. + * 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 Sebastian Bergmann nor the names of his + * 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 OWNER OR 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. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/whatthejeff/PHPExporter + */ + +namespace PHPExporter; + +/** + * Exception for PHPExporter runtime errors. + * + * @package PHPExporter + * @author Sebastian Bergmann + * @copyright 2001-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/whatthejeff/PHPExporter + */ +class ExporterException extends \RuntimeException +{ +} diff --git a/tests/PHPExporter/ExporterTest.php b/tests/PHPExporter/ExporterTest.php new file mode 100644 index 0000000..5d4ba44 --- /dev/null +++ b/tests/PHPExporter/ExporterTest.php @@ -0,0 +1,306 @@ +. + * 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 Sebastian Bergmann nor the names of his + * 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 OWNER OR 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. + * + * @package PHPExporter + * @author Bernhard Schussek + * @copyright 2001-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/whatthejeff/PHPExporter + */ + +use PHPExporter\Exporter; + +/** + * + * + * @package PHPExporter + * @author Bernhard Schussek + * @copyright 2001-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/whatthejeff/PHPExporter + */ +class ExporterTest extends PHPUnit_Framework_TestCase +{ + /** + * Removes spaces in front newlines + * + * @param string $string + * @return string + */ + public static function trimnl($string) + { + return preg_replace('/[ ]*\n/', "\n", $string); + } + + public function exportProvider() + { + $obj2 = new stdClass; + $obj2->foo = 'bar'; + + $obj = new stdClass; + //@codingStandardsIgnoreStart + $obj->null = NULL; + //@codingStandardsIgnoreEnd + $obj->boolean = TRUE; + $obj->integer = 1; + $obj->double = 1.2; + $obj->string = '1'; + $obj->text = "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext"; + $obj->object = $obj2; + $obj->objectagain = $obj2; + $obj->array = array('foo' => 'bar'); + $obj->self = $obj; + + $array = array( + 0 => 0, + 'null' => NULL, + 'boolean' => TRUE, + 'integer' => 1, + 'double' => 1.2, + 'string' => '1', + 'text' => "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", + 'object' => $obj2, + 'objectagain' => $obj2, + 'array' => array('foo' => 'bar'), + ); + + $array['self'] = &$array; + + return array( + array(NULL, 'null'), + array(TRUE, 'true'), + array(1, '1'), + array(1.0, '1.0'), + array(1.2, '1.2'), + array(fopen('php://memory', 'r'), 'resource(%d) of type (stream)'), + array('1', "'1'"), + array(array(array(1,2,3), array(3,4,5)), +<< Array &1 ( + 0 => 1 + 1 => 2 + 2 => 3 + ) + 1 => Array &2 ( + 0 => 3 + 1 => 4 + 2 => 5 + ) +) +EOF + ), + // \n\r and \r is converted to \n + array("this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", +<< null + 'boolean' => true + 'integer' => 1 + 'double' => 1.2 + 'string' => '1' + 'text' => 'this +is +a +very +very +very +very +very +very +long +text' + 'object' => stdClass Object &%x ( + 'foo' => 'bar' + ) + 'objectagain' => stdClass Object &%x + 'array' => Array &%d ( + 'foo' => 'bar' + ) + 'self' => stdClass Object &%x +) +EOF + ), + array(array(), 'Array &%d ()'), + array($array, +<< 0 + 'null' => null + 'boolean' => true + 'integer' => 1 + 'double' => 1.2 + 'string' => '1' + 'text' => 'this +is +a +very +very +very +very +very +very +long +text' + 'object' => stdClass Object &%x ( + 'foo' => 'bar' + ) + 'objectagain' => stdClass Object &%x + 'array' => Array &%d ( + 'foo' => 'bar' + ) + 'self' => Array &%d ( + 0 => 0 + 'null' => null + 'boolean' => true + 'integer' => 1 + 'double' => 1.2 + 'string' => '1' + 'text' => 'this +is +a +very +very +very +very +very +very +long +text' + 'object' => stdClass Object &%x + 'objectagain' => stdClass Object &%x + 'array' => Array &%d ( + 'foo' => 'bar' + ) + 'self' => Array &%d + ) +) +EOF + ), + array( + chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5), + 'Binary String: 0x000102030405' + ), + array( + implode('', array_map('chr', range(0x0e, 0x1f))), + 'Binary String: 0x0e0f101112131415161718191a1b1c1d1e1f' + ), + array( + chr(0x00) . chr(0x09), + 'Binary String: 0x0009' + ), + array( + '', + "''" + ), + ); + } + + /** + * @dataProvider exportProvider + */ + public function testExport($value, $expected) + { + $this->assertStringMatchesFormat($expected, self::trimnl(Exporter::export($value))); + } + + public function shortenedExportProvider() + { + $obj = new stdClass; + $obj->foo = 'bar'; + + $array = array( + 'foo' => 'bar', + ); + + return array( + array(NULL, 'null'), + array(TRUE, 'true'), + array(1, '1'), + array(1.0, '1.0'), + array(1.2, '1.2'), + array('1', "'1'"), + // \n\r and \r is converted to \n + array("this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", "'this\\nis\\na\\nvery\\nvery\\nvery\\nvery...g\\ntext'"), + array(new stdClass, 'stdClass Object ()'), + array($obj, 'stdClass Object (...)'), + array(array(), 'Array ()'), + array($array, 'Array (...)'), + ); + } + + /** + * @dataProvider shortenedExportProvider + */ + public function testShortenedExport($value, $expected) + { + $this->assertSame($expected, self::trimnl(Exporter::shortenedExport($value))); + } + + public function provideNonBinaryMultibyteStrings() + { + return array( + array(implode('', array_map('chr', range(0x09, 0x0d))), 5), + array(implode('', array_map('chr', range(0x20, 0x7f))), 96), + array(implode('', array_map('chr', range(0x80, 0xff))), 128), + ); + } + + + /** + * @dataProvider provideNonBinaryMultibyteStrings + */ + public function testNonBinaryStringExport($value, $expectedLength) + { + $this->assertRegExp("~'.{{$expectedLength}}'\$~s", Exporter::export($value)); + } +}