text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Amf
 * @subpackage Parse_Amf3
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Serializer.php 23775 2011-03-01 17:25:24Z ralph $
 */

/** Zend_Amf_Constants */
require_once 'Zend/Amf/Constants.php';

/** Zend_Amf_Parse_Serializer */
require_once 'Zend/Amf/Parse/Serializer.php';

/** Zend_Amf_Parse_TypeLoader */
require_once 'Zend/Amf/Parse/TypeLoader.php';

/**
 * Detect PHP object type and convert it to a corresponding AMF3 object type
 *
 * @package    Zend_Amf
 * @subpackage Parse_Amf3
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
{
    /**
     * A constant empty string
     * @var string
     */
    protected $_strEmpty = '';

    /**
     * An array of reference objects per amf body
     * @var array
     */
    protected $_referenceObjects = array();

    /**
     * An array of reference strings per amf body
     * @var array
     */
    protected $_referenceStrings = array();

    /**
     * An array of reference class definitions, indexed by classname
     * @var array
     */
    protected $_referenceDefinitions = array();

    /**
     * Serialize PHP types to AMF3 and write to stream
     *
     * Checks to see if the type was declared and then either
     * auto negotiates the type or use the user defined markerType to
     * serialize the data from php back to AMF3
     *
     * @param  mixed $data
     * @param  int $markerType
     * @param  mixed $dataByVal
     * @return void
     */
    public function writeTypeMarker(&$data, $markerType = null, $dataByVal = false)
    {
        // Workaround for PHP5 with E_STRICT enabled complaining about "Only
        // variables should be passed by reference"
        if ((null === $data) && ($dataByVal !== false)) {
            $data = &$dataByVal;
        }
        if (null !== $markerType) {
            // Write the Type Marker to denote the following action script data type
            $this->_stream->writeByte($markerType);

            switch ($markerType) {
                case Zend_Amf_Constants::AMF3_NULL:
                    break;
                case Zend_Amf_Constants::AMF3_BOOLEAN_FALSE:
                    break;
                case Zend_Amf_Constants::AMF3_BOOLEAN_TRUE:
                    break;
                case Zend_Amf_Constants::AMF3_INTEGER:
                    $this->writeInteger($data);
                    break;
                case Zend_Amf_Constants::AMF3_NUMBER:
                    $this->_stream->writeDouble($data);
                    break;
                case Zend_Amf_Constants::AMF3_STRING:
                    $this->writeString($data);
                    break;
                case Zend_Amf_Constants::AMF3_DATE:
                    $this->writeDate($data);
                    break;
                case Zend_Amf_Constants::AMF3_ARRAY:
                    $this->writeArray($data);
                    break;
                case Zend_Amf_Constants::AMF3_OBJECT:
                    $this->writeObject($data);
                    break;
                case Zend_Amf_Constants::AMF3_BYTEARRAY:
                    $this->writeByteArray($data);
                    break;
                case Zend_Amf_Constants::AMF3_XMLSTRING;
                    $this->writeXml($data);
                    break;
                default:
                    require_once 'Zend/Amf/Exception.php';
                    throw new Zend_Amf_Exception('Unknown Type Marker: ' . $markerType);
            }
        } else {
            // Detect Type Marker
            if (is_resource($data)) {
                $data = Zend_Amf_Parse_TypeLoader::handleResource($data);
            }
            switch (true) {
                case (null === $data):
                    $markerType = Zend_Amf_Constants::AMF3_NULL;
                    break;
                case (is_bool($data)):
                    if ($data){
                        $markerType = Zend_Amf_Constants::AMF3_BOOLEAN_TRUE;
                    } else {
                        $markerType = Zend_Amf_Constants::AMF3_BOOLEAN_FALSE;
                    }
                    break;
                case (is_int($data)):
                    if (($data > 0xFFFFFFF) || ($data < -268435456)) {
                        $markerType = Zend_Amf_Constants::AMF3_NUMBER;
                    } else {
                        $markerType = Zend_Amf_Constants::AMF3_INTEGER;
                    }
                    break;
                case (is_float($data)):
                    $markerType = Zend_Amf_Constants::AMF3_NUMBER;
                    break;
                case (is_string($data)):
                    $markerType = Zend_Amf_Constants::AMF3_STRING;
                    break;
                case (is_array($data)):
                    $markerType = Zend_Amf_Constants::AMF3_ARRAY;
                    break;
                case (is_object($data)):
                    // Handle object types.
                    if (($data instanceof DateTime) || ($data instanceof Zend_Date)) {
                        $markerType = Zend_Amf_Constants::AMF3_DATE;
                    } else if ($data instanceof Zend_Amf_Value_ByteArray) {
                        $markerType = Zend_Amf_Constants::AMF3_BYTEARRAY;
                    } else if (($data instanceof DOMDocument) || ($data instanceof SimpleXMLElement)) {
                        $markerType = Zend_Amf_Constants::AMF3_XMLSTRING;
                    } else {
                        $markerType = Zend_Amf_Constants::AMF3_OBJECT;
                    }
                    break;
                default:
                    require_once 'Zend/Amf/Exception.php';
                    throw new Zend_Amf_Exception('Unsupported data type: ' . gettype($data));
            }
            $this->writeTypeMarker($data, $markerType);
        }
    }

    /**
     * Write an AMF3 integer
     *
     * @param int|float $data
     * @return Zend_Amf_Parse_Amf3_Serializer
     */
    public function writeInteger($int)
    {
        if (($int & 0xffffff80) == 0) {
            $this->_stream->writeByte($int & 0x7f);
            return $this;
        }

        if (($int & 0xffffc000) == 0 ) {
            $this->_stream->writeByte(($int >> 7 ) | 0x80);
            $this->_stream->writeByte($int & 0x7f);
            return $this;
        }

        if (($int & 0xffe00000) == 0) {
            $this->_stream->writeByte(($int >> 14 ) | 0x80);
            $this->_stream->writeByte(($int >> 7 ) | 0x80);
            $this->_stream->writeByte($int & 0x7f);
            return $this;
        }

        $this->_stream->writeByte(($int >> 22 ) | 0x80);
        $this->_stream->writeByte(($int >> 15 ) | 0x80);
        $this->_stream->writeByte(($int >> 8 ) | 0x80);
        $this->_stream->writeByte($int & 0xff);
        return $this;
    }

    /**
     * Send string to output stream, without trying to reference it.
     * The string is prepended with strlen($string) << 1 | 0x01
     *
     * @param  string $string
     * @return Zend_Amf_Parse_Amf3_Serializer
     */
    protected function writeBinaryString(&$string){
        $ref = strlen($string) << 1 | 0x01;
        $this->writeInteger($ref);
        $this->_stream->writeBytes($string);

        return $this;
    }

    /**
     * Send string to output stream
     *
     * @param  string $string
     * @return Zend_Amf_Parse_Amf3_Serializer
     */
    public function writeString(&$string)
    {
        $len = strlen($string);
        if(!$len){
            $this->writeInteger(0x01);
            return $this;
        }

        $ref = array_key_exists($string, $this->_referenceStrings)
             ? $this->_referenceStrings[$string]
             : false;
        if ($ref === false){
            $this->_referenceStrings[$string] = count($this->_referenceStrings);
            $this->writeBinaryString($string);
        } else {
            $ref <<= 1;
            $this->writeInteger($ref);
        }

        return $this;
    }

    /**
     * Send ByteArray to output stream
     *
     * @param  string|Zend_Amf_Value_ByteArray  $data
     * @return Zend_Amf_Parse_Amf3_Serializer
     */
    public function writeByteArray(&$data)
    {
        if ($this->writeObjectReference($data)) {
            return $this;
        }

        if (is_string($data)) {
            //nothing to do
        } else if ($data instanceof Zend_Amf_Value_ByteArray) {
            $data = $data->getData();
        } else {
            require_once 'Zend/Amf/Exception.php';
            throw new Zend_Amf_Exception('Invalid ByteArray specified; must be a string or Zend_Amf_Value_ByteArray');
        }

        $this->writeBinaryString($data);

        return $this;
    }

    /**
     * Send xml to output stream
     *
     * @param  DOMDocument|SimpleXMLElement  $xml
     * @return Zend_Amf_Parse_Amf3_Serializer
     */
    public function writeXml($xml)
    {
        if ($this->writeObjectReference($xml)) {
            return $this;
        }

        if(is_string($xml)) {
            //nothing to do
        } else if ($xml instanceof DOMDocument) {
            $xml = $xml->saveXml();
        } else if ($xml instanceof SimpleXMLElement) {
            $xml = $xml->asXML();
        } else {
            require_once 'Zend/Amf/Exception.php';
            throw new Zend_Amf_Exception('Invalid xml specified; must be a DOMDocument or SimpleXMLElement');
        }

        $this->writeBinaryString($xml);

        return $this;
    }

    /**
     * Convert DateTime/Zend_Date to AMF date
     *
     * @param  DateTime|Zend_Date $date
     * @return Zend_Amf_Parse_Amf3_Serializer
     */
    public function writeDate($date)
    {
        if ($this->writeObjectReference($date)) {
            return $this;
        }

        if ($date instanceof DateTime) {
            $dateString = $date->format('U') * 1000;
        } elseif ($date instanceof Zend_Date) {
            $dateString = $date->toString('U') * 1000;
        } else {
            require_once 'Zend/Amf/Exception.php';
            throw new Zend_Amf_Exception('Invalid date specified; must be a string DateTime or Zend_Date object');
        }

        $this->writeInteger(0x01);
        // write time to stream minus milliseconds
        $this->_stream->writeDouble($dateString);
        return $this;
    }

    /**
     * Write a PHP array back to the amf output stream
     *
     * @param array $array
     * @return Zend_Amf_Parse_Amf3_Serializer
     */
    public function writeArray(&$array)
    {
        // arrays aren't reference here but still counted
        $this->_referenceObjects[] = $array;

        // have to seperate mixed from numberic keys.
        $numeric = array();
        $string  = array();
        foreach ($array as $key => &$value) {
            if (is_int($key)) {
                $numeric[] = $value;
            } else {
                $string[$key] = $value;
            }
        }

        // write the preamble id of the array
        $length = count($numeric);
        $id     = ($length << 1) | 0x01;
        $this->writeInteger($id);

        //Write the mixed type array to the output stream
        foreach($string as $key => &$value) {
            $this->writeString($key)
                 ->writeTypeMarker($value);
        }
        $this->writeString($this->_strEmpty);

        // Write the numeric array to ouput stream
        foreach($numeric as &$value) {
            $this->writeTypeMarker($value);
        }
        return $this;
    }

    /**
     * Check if the given object is in the reference table, write the reference if it exists,
     * otherwise add the object to the reference table
     *
     * @param mixed $object object reference to check for reference
     * @param mixed $objectByVal object to check for reference
     * @return Boolean true, if the reference was written, false otherwise
     */
    protected function writeObjectReference(&$object, $objectByVal = false)
    {
        // Workaround for PHP5 with E_STRICT enabled complaining about "Only
        // variables should be passed by reference"
        if ((null === $object) && ($objectByVal !== false)) {
            $object = &$objectByVal;
        }

        $hash = spl_object_hash($object);
        $ref = array_key_exists($hash, $this->_referenceObjects)
             ? $this->_referenceObjects[$hash]
             : false;

        // quickly handle object references
        if ($ref !== false){
            $ref <<= 1;
            $this->writeInteger($ref);
            return true;
        }
        $this->_referenceObjects[$hash] = count($this->_referenceObjects);
        return false;
    }

    /**
     * Write object to ouput stream
     *
     * @param  mixed $data
     * @return Zend_Amf_Parse_Amf3_Serializer
     */
    public function writeObject($object)
    {
        if($this->writeObjectReference($object)){
            return $this;
        }

        $className = '';

        //Check to see if the object is a typed object and we need to change
        switch (true) {
             // the return class mapped name back to actionscript class name.
            case ($className = Zend_Amf_Parse_TypeLoader::getMappedClassName(get_class($object))):
                break;

            // Check to see if the user has defined an explicit Action Script type.
            case isset($object->_explicitType):
                $className = $object->_explicitType;
                break;

            // Check if user has defined a method for accessing the Action Script type
            case method_exists($object, 'getASClassName'):
                $className = $object->getASClassName();
                break;

            // No return class name is set make it a generic object
            case ($object instanceof stdClass):
                $className = '';
                break;

             // By default, use object's class name
            default:
                $className = get_class($object);
                break;
        }

        $writeTraits = true;

        //check to see, if we have a corresponding definition
        if(array_key_exists($className, $this->_referenceDefinitions)){
            $traitsInfo    = $this->_referenceDefinitions[$className]['id'];
            $encoding      = $this->_referenceDefinitions[$className]['encoding'];
            $propertyNames = $this->_referenceDefinitions[$className]['propertyNames'];

            $traitsInfo = ($traitsInfo << 2) | 0x01;

            $writeTraits = false;
        } else {
            $propertyNames = array();

            if($className == ''){
                //if there is no className, we interpret the class as dynamic without any sealed members
                $encoding = Zend_Amf_Constants::ET_DYNAMIC;
            } else {
                $encoding = Zend_Amf_Constants::ET_PROPLIST;

                foreach($object as $key => $value) {
                    if( $key[0] != "_") {
                        $propertyNames[] = $key;
                    }
                }
            }

            $this->_referenceDefinitions[$className] = array(
                        'id'            => count($this->_referenceDefinitions),
                        'encoding'      => $encoding,
                        'propertyNames' => $propertyNames,
                    );

            $traitsInfo = Zend_Amf_Constants::AMF3_OBJECT_ENCODING;
            $traitsInfo |= $encoding << 2;
            $traitsInfo |= (count($propertyNames) << 4);
        }

        $this->writeInteger($traitsInfo);

        if($writeTraits){
            $this->writeString($className);
            foreach ($propertyNames as $value) {
                $this->writeString($value);
            }
        }

        try {
            switch($encoding) {
                case Zend_Amf_Constants::ET_PROPLIST:
                    //Write the sealed values to the output stream.
                    foreach ($propertyNames as $key) {
                        $this->writeTypeMarker($object->$key);
                    }
                    break;
                case Zend_Amf_Constants::ET_DYNAMIC:
                    //Write the sealed values to the output stream.
                    foreach ($propertyNames as $key) {
                        $this->writeTypeMarker($object->$key);
                    }

                    //Write remaining properties
                    foreach($object as $key => $value){
                        if(!in_array($key,$propertyNames) && $key[0] != "_"){
                            $this->writeString($key);
                            $this->writeTypeMarker($value);
                        }
                    }

                    //Write an empty string to end the dynamic part
                    $this->writeString($this->_strEmpty);
                    break;
                case Zend_Amf_Constants::ET_EXTERNAL:
                    require_once 'Zend/Amf/Exception.php';
                    throw new Zend_Amf_Exception('External Object Encoding not implemented');
                    break;
                default:
                    require_once 'Zend/Amf/Exception.php';
                    throw new Zend_Amf_Exception('Unknown Object Encoding type: ' . $encoding);
            }
        } catch (Exception $e) {
            require_once 'Zend/Amf/Exception.php';
            throw new Zend_Amf_Exception('Unable to writeObject output: ' . $e->getMessage(), 0, $e);
        }

        return $this;
    }
}


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Amf
 * @subpackage Parse_Amf3
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Deserializer.php 24386 2011-08-15 18:24:49Z padraic $
 */

/** Zend_Amf_Parse_Deserializer */
require_once 'Zend/Amf/Parse/Deserializer.php';

/** Zend_Amf_Parse_TypeLoader */
require_once 'Zend/Amf/Parse/TypeLoader.php';

/**
 * Read an AMF3 input stream and convert it into PHP data types.
 *
 * @todo       readObject to handle Typed Objects
 * @todo       readXMLStrimg to be implemented.
 * @todo       Class could be implemented as Factory Class with each data type it's own class.
 * @package    Zend_Amf
 * @subpackage Parse_Amf3
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Amf_Parse_Amf3_Deserializer extends Zend_Amf_Parse_Deserializer
{
    /**
     * Total number of objects in the referenceObject array
     * @var int
     */
    protected $_objectCount;

    /**
     * An array of reference objects per amf body
     * @var array
     */
    protected $_referenceObjects = array();

    /**
     * An array of reference strings per amf body
     * @var array
     */
    protected $_referenceStrings = array();

    /**
     * An array of reference class definitions per body
     * @var array
     */
    protected $_referenceDefinitions = array();

    /**
     * Read AMF markers and dispatch for deserialization
     *
     * Checks for AMF marker types and calls the appropriate methods
     * for deserializing those marker types. markers are the data type of
     * the following value.
     *
     * @param  integer $typeMarker
     * @return mixed Whatever the corresponding PHP data type is
     * @throws Zend_Amf_Exception for unidentified marker type
     */
    public function readTypeMarker($typeMarker = null)
    {
        if(null === $typeMarker) {
            $typeMarker = $this->_stream->readByte();
        }

        switch($typeMarker) {
            case Zend_Amf_Constants::AMF3_UNDEFINED:
                 return null;
            case Zend_Amf_Constants::AMF3_NULL:
                 return null;
            case Zend_Amf_Constants::AMF3_BOOLEAN_FALSE:
                 return false;
            case Zend_Amf_Constants::AMF3_BOOLEAN_TRUE:
                 return true;
            case Zend_Amf_Constants::AMF3_INTEGER:
                 return $this->readInteger();
            case Zend_Amf_Constants::AMF3_NUMBER:
                 return $this->_stream->readDouble();
            case Zend_Amf_Constants::AMF3_STRING:
                 return $this->readString();
            case Zend_Amf_Constants::AMF3_DATE:
                 return $this->readDate();
            case Zend_Amf_Constants::AMF3_ARRAY:
                 return $this->readArray();
            case Zend_Amf_Constants::AMF3_OBJECT:
                 return $this->readObject();
            case Zend_Amf_Constants::AMF3_XML:
            case Zend_Amf_Constants::AMF3_XMLSTRING:
                 return $this->readXmlString();
            case Zend_Amf_Constants::AMF3_BYTEARRAY:
                 return $this->readString();
            default:
                require_once 'Zend/Amf/Exception.php';
                throw new Zend_Amf_Exception('Unsupported type marker: ' . $typeMarker);
        }
    }

    /**
     * Read and deserialize an integer
     *
     * AMF 3 represents smaller integers with fewer bytes using the most
     * significant bit of each byte. The worst case uses 32-bits
     * to represent a 29-bit number, which is what we would have
     * done with no compression.
     * - 0x00000000 - 0x0000007F : 0xxxxxxx
     * - 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx
     * - 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx
     * - 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx
     * - 0x40000000 - 0xFFFFFFFF : throw range exception
     *
     * 0x04 -> integer type code, followed by up to 4 bytes of data.
     *
     * Parsing integers on OSFlash for the AMF3 integer data format:
     * @link http://osflash.org/amf3/parsing_integers
     * @return int|float
     */
    public function readInteger()
    {
        $count        = 1;
        $intReference = $this->_stream->readByte();
        $result       = 0;
        while ((($intReference & 0x80) != 0) && $count < 4) {
            $result       <<= 7;
            $result        |= ($intReference & 0x7f);
            $intReference   = $this->_stream->readByte();
            $count++;
        }
        if ($count < 4) {
            $result <<= 7;
            $result  |= $intReference;
        } else {
            // Use all 8 bits from the 4th byte
            $result <<= 8;
            $result  |= $intReference;

            // Check if the integer should be negative
            if (($result & 0x10000000) != 0) {
                //and extend the sign bit
                $result |= ~0xFFFFFFF;
            }
        }
        return $result;
    }

    /**
     * Read and deserialize a string
     *
     * Strings can be sent as a reference to a previously
     * occurring String by using an index to the implicit string reference table.
     * Strings are encoding using UTF-8 - however the header may either
     * describe a string literal or a string reference.
     *
     * - string = 0x06 string-data
     * - string-data = integer-data [ modified-utf-8 ]
     * - modified-utf-8 = *OCTET
     *
     * @return String
     */
    public function readString()
    {
        $stringReference = $this->readInteger();

        //Check if this is a reference string
        if (($stringReference & 0x01) == 0) {
            // reference string
            $stringReference = $stringReference >> 1;
            if ($stringReference >= count($this->_referenceStrings)) {
                require_once 'Zend/Amf/Exception.php';
                throw new Zend_Amf_Exception('Undefined string reference: ' . $stringReference);
            }
            // reference string found
            return $this->_referenceStrings[$stringReference];
        }

        $length = $stringReference >> 1;
        if ($length) {
            $string = $this->_stream->readBytes($length);
            $this->_referenceStrings[] = $string;
        } else {
            $string = "";
        }
        return $string;
    }

    /**
     * Read and deserialize a date
     *
     * Data is the number of milliseconds elapsed since the epoch
     * of midnight, 1st Jan 1970 in the UTC time zone.
     * Local time zone information is not sent to flash.
     *
     * - date = 0x08 integer-data [ number-data ]
     *
     * @return Zend_Date
     */
    public function readDate()
    {
        $dateReference = $this->readInteger();
        if (($dateReference & 0x01) == 0) {
            $dateReference = $dateReference >> 1;
            if ($dateReference>=count($this->_referenceObjects)) {
                require_once 'Zend/Amf/Exception.php';
                throw new Zend_Amf_Exception('Undefined date reference: ' . $dateReference);
            }
            return $this->_referenceObjects[$dateReference];
        }

        $timestamp = floor($this->_stream->readDouble() / 1000);

        require_once 'Zend/Date.php';
        $dateTime  = new Zend_Date($timestamp);
        $this->_referenceObjects[] = $dateTime;
        return $dateTime;
    }

    /**
     * Read amf array to PHP array
     *
     * - array = 0x09 integer-data ( [ 1OCTET *amf3-data ] | [OCTET *amf3-data 1] | [ OCTET *amf-data ] )
     *
     * @return array
     */
    public function readArray()
    {
        $arrayReference = $this->readInteger();
        if (($arrayReference & 0x01)==0){
            $arrayReference = $arrayReference >> 1;
            if ($arrayReference>=count($this->_referenceObjects)) {
                require_once 'Zend/Amf/Exception.php';
                throw new Zend_Amf_Exception('Unknow array reference: ' . $arrayReference);
            }
            return $this->_referenceObjects[$arrayReference];
        }

        // Create a holder for the array in the reference list
        $data = array();
        $this->_referenceObjects[] =& $data;
        $key = $this->readString();

        // Iterating for string based keys.
        while ($key != '') {
            $data[$key] = $this->readTypeMarker();
            $key = $this->readString();
        }

        $arrayReference = $arrayReference >>1;

        //We have a dense array
        for ($i=0; $i < $arrayReference; $i++) {
            $data[] = $this->readTypeMarker();
        }

        return $data;
    }

    /**
     * Read an object from the AMF stream and convert it into a PHP object
     *
     * @todo   Rather than using an array of traitsInfo create Zend_Amf_Value_TraitsInfo
     * @return object|array
     */
    public function readObject()
    {
        $traitsInfo   = $this->readInteger();
        $storedObject = ($traitsInfo & 0x01)==0;
        $traitsInfo   = $traitsInfo >> 1;

        // Check if the Object is in the stored Objects reference table
        if ($storedObject) {
            $ref = $traitsInfo;
            if (!isset($this->_referenceObjects[$ref])) {
                require_once 'Zend/Amf/Exception.php';
                throw new Zend_Amf_Exception('Unknown Object reference: ' . $ref);
            }
            $returnObject = $this->_referenceObjects[$ref];
        } else {
            // Check if the Object is in the stored Definitions reference table
            $storedClass = ($traitsInfo & 0x01) == 0;
            $traitsInfo  = $traitsInfo >> 1;
            if ($storedClass) {
                $ref = $traitsInfo;
                if (!isset($this->_referenceDefinitions[$ref])) {
                    require_once 'Zend/Amf/Exception.php';
                    throw new Zend_Amf_Exception('Unknows Definition reference: '. $ref);
                }
                // Populate the reference attributes
                $className     = $this->_referenceDefinitions[$ref]['className'];
                $encoding      = $this->_referenceDefinitions[$ref]['encoding'];
                $propertyNames = $this->_referenceDefinitions[$ref]['propertyNames'];
            } else {
                // The class was not in the reference tables. Start reading rawdata to build traits.
                // Create a traits table. Zend_Amf_Value_TraitsInfo would be ideal
                $className     = $this->readString();
                $encoding      = $traitsInfo & 0x03;
                $propertyNames = array();
                $traitsInfo    = $traitsInfo >> 2;
            }

            // We now have the object traits defined in variables. Time to go to work:
            if (!$className) {
                // No class name generic object
                $returnObject = new stdClass();
            } else {
                // Defined object
                // Typed object lookup against registered classname maps
                if ($loader = Zend_Amf_Parse_TypeLoader::loadType($className)) {
                    $returnObject = new $loader();
                } else {
                    //user defined typed object
                    require_once 'Zend/Amf/Exception.php';
                    throw new Zend_Amf_Exception('Typed object not found: '. $className . ' ');
                }
            }

            // Add the Object to the reference table
            $this->_referenceObjects[] = $returnObject;

            $properties = array(); // clear value
            // Check encoding types for additional processing.
            switch ($encoding) {
                case (Zend_Amf_Constants::ET_EXTERNAL):
                    // Externalizable object such as {ArrayCollection} and {ObjectProxy}
                    if (!$storedClass) {
                        $this->_referenceDefinitions[] = array(
                            'className'     => $className,
                            'encoding'      => $encoding,
                            'propertyNames' => $propertyNames,
                        );
                    }
                    $returnObject->externalizedData = $this->readTypeMarker();
                    break;
                case (Zend_Amf_Constants::ET_DYNAMIC):
                    // used for Name-value encoding
                    if (!$storedClass) {
                        $this->_referenceDefinitions[] = array(
                            'className'     => $className,
                            'encoding'      => $encoding,
                            'propertyNames' => $propertyNames,
                        );
                    }
                    // not a reference object read name value properties from byte stream
                    do {
                        $property = $this->readString();
                        if ($property != "") {
                            $propertyNames[]       = $property;
                            $properties[$property] = $this->readTypeMarker();
                        }
                    } while ($property !="");
                    break;
                default:
                    // basic property list object.
                    if (!$storedClass) {
                        $count = $traitsInfo; // Number of properties in the list
                        for($i=0; $i< $count; $i++) {
                            $propertyNames[] = $this->readString();
                        }
                        // Add a reference to the class.
                        $this->_referenceDefinitions[] = array(
                            'className'     => $className,
                            'encoding'      => $encoding,
                            'propertyNames' => $propertyNames,
                        );
                    }
                    foreach ($propertyNames as $property) {
                        $properties[$property] = $this->readTypeMarker();
                    }
                    break;
            }

            // Add properties back to the return object.
            if (!is_array($properties)) $properties = array();
            foreach($properties as $key=>$value) {
                if($key) {
                    $returnObject->$key = $value;
                }
            }

        }

       if ($returnObject instanceof Zend_Amf_Value_Messaging_ArrayCollection) {
            if (isset($returnObject->externalizedData)) {
                $returnObject = $returnObject->externalizedData;
            } else {
                $returnObject = get_object_vars($returnObject);
            }
       }

        return $returnObject;
    }

    /**
     * Convert XML to SimpleXml
     * If user wants DomDocument they can use dom_import_simplexml
     *
     * @return SimpleXml Object
     */
    public function readXmlString()
    {
        $xmlReference = $this->readInteger();
        $length = $xmlReference >> 1;
        $string = $this->_stream->readBytes($length);
        return simplexml_load_string($string);
    }
}

repo_name: michael-romer/zf-boilerplate

repo_language: PHP

mime_type: text/html


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Amf
 * @subpackage Parse
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Stream.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * This class will convert stream resource to string by just reading it
 *
 * @package    Zend_Amf
 * @subpackage Parse
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Amf_Parse_Resource_Stream
{
    /**
     * Parse resource into string
     *
     * @param resource $resource Stream resource
     * @return array
     */
    public function parse($resource) {
        return stream_get_contents($resource);
    }
}


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Amf
 * @subpackage Parse
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: MysqliResult.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * This class will convert mysql result resource to array suitable for passing
 * to the external entities.
 *
 * @package    Zend_Amf
 * @subpackage Parse
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Amf_Parse_Resource_MysqliResult
{

    /**
     * mapping taken from http://forums.mysql.com/read.php?52,255868,255895#msg-255895
     */
    static public $mysqli_type = array(
        0 => "MYSQLI_TYPE_DECIMAL",
        1 => "MYSQLI_TYPE_TINYINT",
        2 => "MYSQLI_TYPE_SMALLINT",
        3 => "MYSQLI_TYPE_INTEGER",
        4 => "MYSQLI_TYPE_FLOAT",
        5 => "MYSQLI_TYPE_DOUBLE",
        7 => "MYSQLI_TYPE_TIMESTAMP",
        8 => "MYSQLI_TYPE_BIGINT",
        9 => "MYSQLI_TYPE_MEDIUMINT",
        10 => "MYSQLI_TYPE_DATE",
        11 => "MYSQLI_TYPE_TIME",
        12 => "MYSQLI_TYPE_DATETIME",
        13 => "MYSQLI_TYPE_YEAR",
        14 => "MYSQLI_TYPE_DATE",
        16 => "MYSQLI_TYPE_BIT",
        246 => "MYSQLI_TYPE_DECIMAL",
        247 => "MYSQLI_TYPE_ENUM",
        248 => "MYSQLI_TYPE_SET",
        249 => "MYSQLI_TYPE_TINYBLOB",
        250 => "MYSQLI_TYPE_MEDIUMBLOB",
        251 => "MYSQLI_TYPE_LONGBLOB",
        252 => "MYSQLI_TYPE_BLOB",
        253 => "MYSQLI_TYPE_VARCHAR",
        254 => "MYSQLI_TYPE_CHAR",
        255 => "MYSQLI_TYPE_GEOMETRY",
    );

    // Build an associative array for a type look up
    static $mysqli_to_php = array(
        "MYSQLI_TYPE_DECIMAL"     => 'float',
        "MYSQLI_TYPE_NEWDECIMAL"  => 'float',
        "MYSQLI_TYPE_BIT"         => 'integer',
        "MYSQLI_TYPE_TINYINT"     => 'integer',
        "MYSQLI_TYPE_SMALLINT"    => 'integer',
        "MYSQLI_TYPE_MEDIUMINT"   => 'integer',
        "MYSQLI_TYPE_BIGINT"      => 'integer',
        "MYSQLI_TYPE_INTEGER"     => 'integer',
        "MYSQLI_TYPE_FLOAT"       => 'float',
        "MYSQLI_TYPE_DOUBLE"      => 'float',
        "MYSQLI_TYPE_NULL"        => 'null',
        "MYSQLI_TYPE_TIMESTAMP"   => 'string',
        "MYSQLI_TYPE_INT24"       => 'integer',
        "MYSQLI_TYPE_DATE"        => 'string',
        "MYSQLI_TYPE_TIME"        => 'string',
        "MYSQLI_TYPE_DATETIME"    => 'string',
        "MYSQLI_TYPE_YEAR"        => 'string',
        "MYSQLI_TYPE_NEWDATE"     => 'string',
        "MYSQLI_TYPE_ENUM"        => 'string',
        "MYSQLI_TYPE_SET"         => 'string',
        "MYSQLI_TYPE_TINYBLOB"    => 'object',
        "MYSQLI_TYPE_MEDIUMBLOB"  => 'object',
        "MYSQLI_TYPE_LONGBLOB"    => 'object',
        "MYSQLI_TYPE_BLOB"        => 'object',
        "MYSQLI_TYPE_CHAR"        => 'string',
        "MYSQLI_TYPE_VARCHAR"     => 'string',
        "MYSQLI_TYPE_GEOMETRY"    => 'object',
        "MYSQLI_TYPE_BIT"         => 'integer',
    );

    /**
     * Parse resource into array
     *
     * @param resource $resource
     * @return array
     */
    public function parse($resource) {

        $result = array();
        $fieldcnt = mysqli_num_fields($resource);

        $fields_transform = array();

        for($i=0;$i<$fieldcnt;$i++) {
            $finfo = mysqli_fetch_field_direct($resource, $i);

            if(isset(self::$mysqli_type[$finfo->type])) {
                $fields_transform[$finfo->name] = self::$mysqli_to_php[self::$mysqli_type[$finfo->type]];
            }
        }

        while($row = mysqli_fetch_assoc($resource)) {
            foreach($fields_transform as $fieldname => $fieldtype) {
               settype($row[$fieldname], $fieldtype);
            }
            $result[] = $row;
        }
        return $result;
    }
}


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Amf
 * @subpackage Parse
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: MysqlResult.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * This class will convert mysql result resource to array suitable for passing
 * to the external entities.
 *
 * @package    Zend_Amf
 * @subpackage Parse
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Amf_Parse_Resource_MysqlResult
{
    /**
     * @var array List of Mysql types with PHP counterparts
     *
     * Key => Value is Mysql type (exact string) => PHP type
     */
    static public $fieldTypes = array(
        "int" => "int",
        "timestamp" => "int",
        "year" => "int",
        "real" => "float",
    );
    /**
     * Parse resource into array
     *
     * @param resource $resource
     * @return array
     */
    public function parse($resource) {
        $result = array();
        $fieldcnt = mysql_num_fields($resource);
        $fields_transform = array();
        for($i=0;$i<$fieldcnt;$i++) {
            $type = mysql_field_type($resource, $i);
            if(isset(self::$fieldTypes[$type])) {
                $fields_transform[mysql_field_name($resource, $i)] = self::$fieldTypes[$type];
            }
        }

        while($row = mysql_fetch_object($resource)) {
            foreach($fields_transform as $fieldname => $fieldtype) {
               settype($row->$fieldname, $fieldtype);
            }
            $result[] = $row;
        }
        return $result;
    }
}


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category  Zend
 * @package   Zend_Text
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd     New BSD License
 * @version   $Id: Exception.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * Zend_Exception
 */
require_once 'Zend/Exception.php';

/**
 * Exception class for Zend_Text
 *
 * @category  Zend
 * @package   Zend_Text
 * @uses      Zend_Exception
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Text_Exception extends Zend_Exception
{
}


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category  Zend
 * @package   Zend_Text_Figlet
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd     New BSD License
 * @version   $Id: Figlet.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * Zend_Text_Figlet is a PHP implementation of FIGlet
 *
 * @category  Zend
 * @package   Zend_Text_Figlet
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Text_Figlet
{
    /**
     * Smush2 layout modes
     */
    const SM_EQUAL     = 0x01;
    const SM_LOWLINE   = 0x02;
    const SM_HIERARCHY = 0x04;
    const SM_PAIR      = 0x08;
    const SM_BIGX      = 0x10;
    const SM_HARDBLANK = 0x20;
    const SM_KERN      = 0x40;
    const SM_SMUSH     = 0x80;

    /**
     * Smush mode override modes
     */
    const SMO_NO    = 0;
    const SMO_YES   = 1;
    const SMO_FORCE = 2;

    /**
     * Justifications
     */
    const JUSTIFICATION_LEFT   = 0;
    const JUSTIFICATION_CENTER = 1;
    const JUSTIFICATION_RIGHT  = 2;

    /**
     * Write directions
     */
    const DIRECTION_LEFT_TO_RIGHT = 0;
    const DIRECTION_RIGHT_TO_LEFT = 1;

    /**
     * Magic fontfile number
     */
    const FONTFILE_MAGIC_NUMBER = 'flf2';

    /**
     * Array containing all characters of the current font
     *
     * @var array
     */
    protected $_charList = array();

    /**
     * Indicates if a font was loaded yet
     *
     * @var boolean
     */
    protected $_fontLoaded = false;

    /**
     * Latin-1 codes for German letters, respectively:
     *
     * LATIN CAPITAL LETTER A WITH DIAERESIS = A-umlaut
     * LATIN CAPITAL LETTER O WITH DIAERESIS = O-umlaut
     * LATIN CAPITAL LETTER U WITH DIAERESIS = U-umlaut
     * LATIN SMALL LETTER A WITH DIAERESIS = a-umlaut
     * LATIN SMALL LETTER O WITH DIAERESIS = o-umlaut
     * LATIN SMALL LETTER U WITH DIAERESIS = u-umlaut
     * LATIN SMALL LETTER SHARP S = ess-zed
     *
     * @var array
     */
    protected $_germanChars = array(196, 214, 220, 228, 246, 252, 223);

    /**
     * Output width, defaults to 80.
     *
     * @var integer
     */
    protected $_outputWidth = 80;

    /**
     * Hard blank character
     *
     * @var string
     */
    protected $_hardBlank;

    /**
     * Height of the characters
     *
     * @var integer
     */
    protected $_charHeight;

    /**
     * Max length of any character
     *
     * @var integer
     */
    protected $_maxLength;

    /**
     * Smush mode
     *
     * @var integer
     */
    protected $_smushMode = 0;

    /**
     * Smush defined by the font
     *
     * @var integer
     */
    protected $_fontSmush = 0;

    /**
     * Smush defined by the user
     *
     * @var integer
     */
    protected $_userSmush = 0;

    /**
     * Wether to handle paragraphs || not
     *
     * @var boolean
     */
    protected $_handleParagraphs = false;

    /**
     * Justification for the text, according to $_outputWidth
     *
     * For using font default, this parameter should be null, else one of
     * the values of Zend_Text_Figlet::JUSTIFICATION_*
     *
     * @var integer
     */
    protected $_justification = null;

    /**
     * Direction of text-writing, namely right to left
     *
     * For using font default, this parameter should be null, else one of
     * the values of Zend_Text_Figlet::DIRECTION_*
     *
     * @var integer
     */
    protected $_rightToLeft = null;

    /**
     * Override font file smush layout
     *
     * @var integer
     */
    protected $_smushOverride = 0;

    /**
     * Options of the current font
     *
     * @var array
     */
    protected $_fontOptions = array();

    /**
     * Previous character width
     *
     * @var integer
     */
    protected $_previousCharWidth = 0;

    /**
     * Current character width
     *
     * @var integer
     */
    protected $_currentCharWidth = 0;

    /**
     * Current outline length
     *
     * @var integer
     */
    protected $_outlineLength = 0;

    /**
     * Maxmimum outline length
     *
     * @var integer
     */
    protected $_outlineLengthLimit = 0;

    /**
     * In character line
     *
     * @var string
     */
    protected $_inCharLine;

    /**
     * In character line length
     *
     * @var integer
     */
    protected $_inCharLineLength = 0;

    /**
     * Maximum in character line length
     *
     * @var integer
     */
    protected $_inCharLineLengthLimit = 0;

    /**
     * Current char
     *
     * @var array
     */
    protected $_currentChar = null;

    /**
     * Current output line
     *
     * @var array
     */
    protected $_outputLine;

    /**
     * Current output
     *
     * @var string
     */
    protected $_output;

    /**
     * Option keys to skip when calling setOptions()
     *
     * @var array
     */
    protected $_skipOptions = array(
        'options',
        'config',
    );

    /**
     * Instantiate the FIGlet with a specific font. If no font is given, the
     * standard font is used. You can also supply multiple options via
     * the $options variable, which can either be an array or an instance of
     * Zend_Config.
     *
     * @param array|Zend_Config $options Options for the output
     */
    public function __construct($options = null)
    {
        // Set options
        if (is_array($options)) {
            $this->setOptions($options);
        } else if ($options instanceof Zend_Config) {
            $this->setConfig($options);
        }

        // If no font was defined, load default font
        if (!$this->_fontLoaded) {
            $this->_loadFont(dirname(__FILE__) . '/Figlet/zend-framework.flf');
        }
    }

    /**
     * Set options from array
     *
     * @param  array $options Configuration for Zend_Text_Figlet
     * @return Zend_Text_Figlet
     */
    public function setOptions(array $options)
    {
        foreach ($options as $key => $value) {
            if (in_array(strtolower($key), $this->_skipOptions)) {
                continue;
            }

            $method = 'set' . ucfirst($key);
            if (method_exists($this, $method)) {
                $this->$method($value);
            }
        }
        return $this;
    }

    /**
     * Set options from config object
     *
     * @param  Zend_Config $config Configuration for Zend_Text_Figlet
     * @return Zend_Text_Figlet
     */
    public function setConfig(Zend_Config $config)
    {
        return $this->setOptions($config->toArray());
    }

    /**
     * Set a font to use
     *
     * @param  string $font Path to the font
     * @return Zend_Text_Figlet
     */
    public function setFont($font)
    {
        $this->_loadFont($font);
        return $this;
    }

    /**
     * Set handling of paragraphs
     *
     * @param  boolean $handleParagraphs Wether to handle paragraphs or not
     * @return Zend_Text_Figlet
     */
    public function setHandleParagraphs($handleParagraphs)
    {
        $this->_handleParagraphs = (bool) $handleParagraphs;
        return $this;
    }

    /**
     * Set the justification. 0 stands for left aligned, 1 for centered and 2
     * for right aligned.
     *
     * @param  integer $justification Justification of the output text
     * @return Zend_Text_Figlet
     */
    public function setJustification($justification)
    {
        $this->_justification = min(3, max(0, (int) $justification));
        return $this;
    }

    /**
     * Set the output width
     *
     * @param  integer $outputWidth Output with which should be used for word
     *                              wrapping and justification
     * @return Zend_Text_Figlet
     */
    public function setOutputWidth($outputWidth)
    {
        $this->_outputWidth = max(1, (int) $outputWidth);
        return $this;
    }

    /**
     * Set right to left mode. For writing from left to right, use
     * Zend_Text_Figlet::DIRECTION_LEFT_TO_RIGHT. For writing from right to left,
     * use Zend_Text_Figlet::DIRECTION_RIGHT_TO_LEFT.
     *
     * @param  integer $rightToLeft Right-to-left mode
     * @return Zend_Text_Figlet
     */
    public function setRightToLeft($rightToLeft)
    {
        $this->_rightToLeft = min(1, max(0, (int) $rightToLeft));
        return $this;
    }

    /**
     * Set the smush mode.
     *
     * Use one of the constants of Zend_Text_Figlet::SM_*, you may combine them.
     *
     * @param  integer $smushMode Smush mode to use for generating text
     * @return Zend_Text_Figlet
     */
    public function setSmushMode($smushMode)
    {
        $smushMode = (int) $smushMode;

        if ($smushMode < -1) {
            $this->_smushOverride = self::SMO_NO;
        } else {
            if ($smushMode === 0) {
                $this->_userSmush = self::SM_KERN;
            } else if ($smushMode === -1) {
                $this->_userSmush = 0;
            } else {
                $this->_userSmush = (($smushMode & 63) | self::SM_SMUSH);
            }

            $this->_smushOverride = self::SMO_YES;
        }

        $this->_setUsedSmush();

        return $this;
    }

    /**
     * Render a FIGlet text
     *
     * @param  string $text     Text to convert to a figlet text
     * @param  string $encoding Encoding of the input string
     * @throws InvalidArgumentException When $text is not a string
     * @throws Zend_Text_Figlet_Exception    When $text it not properly encoded
     * @return string
     */
    public function render($text, $encoding = 'UTF-8')
    {
        if (!is_string($text)) {
            throw new InvalidArgumentException('$text must be a string');
        }

        if ($encoding !== 'UTF-8') {
            $text = iconv($encoding, 'UTF-8', $text);
        }

        $this->_output     = '';
        $this->_outputLine = array();

        $this->_clearLine();

        $this->_outlineLengthLimit    = ($this->_outputWidth - 1);
        $this->_inCharLineLengthLimit = ($this->_outputWidth * 4 + 100);

        $wordBreakMode  = 0;
        $lastCharWasEol = false;
        $textLength     = @iconv_strlen($text, 'UTF-8');

        if ($textLength === false) {
            require_once 'Zend/Text/Figlet/Exception.php';
            throw new Zend_Text_Figlet_Exception('$text is not encoded with ' . $encoding);
        }

        for ($charNum = 0; $charNum < $textLength; $charNum++) {
            // Handle paragraphs
            $char = iconv_substr($text, $charNum, 1, 'UTF-8');

            if ($char === "\n" && $this->_handleParagraphs && !$lastCharWasEol) {
                $nextChar = iconv_substr($text, ($charNum + 1), 1, 'UTF-8');
                if (!$nextChar) {
                    $nextChar = null;
                }

                $char = (ctype_space($nextChar)) ? "\n" : ' ';
            }

            $lastCharWasEol = (ctype_space($char) && $char !== "\t" && $char !== ' ');

            if (ctype_space($char)) {
                $char = ($char === "\t" || $char === ' ') ? ' ': "\n";
            }

            // Skip unprintable characters
            $ordChar = $this->_uniOrd($char);
            if (($ordChar > 0 && $ordChar < 32 && $char !== "\n") || $ordChar === 127) {
                continue;
            }

            // Build the character
            // Note: The following code is complex and thoroughly tested.
            // Be careful when modifying!
            do {
                $charNotAdded = false;

                if ($wordBreakMode === -1) {
                    if ($char === ' ') {
                        break;
                    } else if ($char === "\n") {
                        $wordBreakMode = 0;
                        break;
                    }

                    $wordBreakMode = 0;
                }

                if ($char === "\n") {
                    $this->_appendLine();
                    $wordBreakMode = false;
                } else if ($this->_addChar($char)) {
                    if ($char !== ' ') {
                        $wordBreakMode = ($wordBreakMode >= 2) ? 3: 1;
                    } else {
                        $wordBreakMode = ($wordBreakMode > 0) ? 2: 0;
                    }
                } else if ($this->_outlineLength === 0) {
                    for ($i = 0; $i < $this->_charHeight; $i++) {
                        if ($this->_rightToLeft === 1 && $this->_outputWidth > 1) {
                            $offset = (strlen($this->_currentChar[$i]) - $this->_outlineLengthLimit);
                            $this->_putString(substr($this->_currentChar[$i], $offset));
                        } else {
                            $this->_putString($this->_currentChar[$i]);
                        }
                    }

                    $wordBreakMode = -1;
                } else if ($char === ' ') {
                    if ($wordBreakMode === 2) {
                        $this->_splitLine();
                    } else {
                        $this->_appendLine();
                    }

                    $wordBreakMode = -1;
                } else {
                    if ($wordBreakMode >= 2) {
                        $this->_splitLine();
                    } else {
                        $this->_appendLine();
                    }

                    $wordBreakMode = ($wordBreakMode === 3) ? 1 : 0;
                    $charNotAdded  = true;
                }
            } while ($charNotAdded);
        }

        if ($this->_outlineLength !== 0) {
            $this->_appendLine();
        }

        return $this->_output;
    }

    /**
     * Puts the given string, substituting blanks for hardblanks. If outputWidth
     * is 1, puts the entire string; otherwise puts at most outputWidth - 1
     * characters. Puts a newline at the end of the string. The string is left-
     * justified, centered or right-justified (taking outputWidth as the screen
     * width) if justification is 0, 1 or 2 respectively.
     *
     * @param  string $string The string to add to the output
     * @return void
     */
    protected function _putString($string)
    {
        $length = strlen($string);

        if ($this->_outputWidth > 1) {
            if ($length > ($this->_outputWidth - 1)) {
                $length = ($this->_outputWidth - 1);
            }

            if ($this->_justification > 0) {
                for ($i = 1;
                     ((3 - $this->_justification) * $i + $length + $this->_justification - 2) < $this->_outputWidth;
                     $i++) {
                    $this->_output .= ' ';
                }
            }
        }

        $this->_output .= str_replace($this->_hardBlank, ' ', $string) . "\n";
    }

    /**
     * Appends the current line to the output
     *
     * @return void
     */
    protected function _appendLine()
    {
        for ($i = 0; $i < $this->_charHeight; $i++) {
            $this->_putString($this->_outputLine[$i]);
        }

        $this->_clearLine();
    }

    /**
     * Splits inCharLine at the last word break (bunch of consecutive blanks).
     * Makes a new line out of the first part and appends it using appendLine().
     * Makes a new line out of the second part and returns.
     *
     * @return void
     */
    protected function _splitLine()
    {
        $gotSpace = false;
        for ($i = ($this->_inCharLineLength - 1); $i >= 0; $i--) {
            if (!$gotSpace && $this->_inCharLine[$i] === ' ') {
                $gotSpace  = true;
                $lastSpace = $i;
            }

            if ($gotSpace && $this->_inCharLine[$i] !== ' ') {
                break;
            }
        }

        $firstLength = ($i + 1);
        $lastLength  = ($this->_inCharLineLength - $lastSpace - 1);

        $firstPart = '';
        for ($i = 0; $i < $firstLength; $i++) {
            $firstPart[$i] = $this->_inCharLine[$i];
        }

        $lastPart = '';
        for ($i = 0; $i < $lastLength; $i++) {
            $lastPart[$i] = $this->_inCharLine[($lastSpace + 1 + $i)];
        }

        $this->_clearLine();

        for ($i = 0; $i < $firstLength; $i++) {
            $this->_addChar($firstPart[$i]);
        }

        $this->_appendLine();

        for ($i = 0; $i < $lastLength; $i++) {
            $this->_addChar($lastPart[$i]);
        }
    }

    /**
     * Clears the current line
     *
     * @return void
     */
    protected function _clearLine()
    {
        for ($i = 0; $i < $this->_charHeight; $i++) {
            $this->_outputLine[$i] = '';
        }

        $this->_outlineLength    = 0;
        $this->_inCharLineLength = 0;
    }

    /**
     * Attempts to add the given character onto the end of the current line.
     * Returns true if this can be done, false otherwise.
     *
     * @param  string $char Character which to add to the output
     * @return boolean
     */
    protected function _addChar($char)
    {
        $this->_getLetter($char);

        if ($this->_currentChar === null) {
            return true;
        }

        $smushAmount = $this->_smushAmount();

        if (($this->_outlineLength + $this->_currentCharWidth - $smushAmount) > $this->_outlineLengthLimit
            || ($this->_inCharLineLength + 1) > $this->_inCharLineLengthLimit) {
            return false;
        }

        $tempLine = '';
        for ($row = 0; $row < $this->_charHeight; $row++) {
            if ($this->_rightToLeft === 1) {
                $tempLine = $this->_currentChar[$row];

                for ($k = 0; $k < $smushAmount; $k++) {
                    $position            = ($this->_currentCharWidth - $smushAmount + $k);
                    $tempLine[$position] = $this->_smushem($tempLine[$position], $this->_outputLine[$row][$k]);
                }

                $this->_outputLine[$row] = $tempLine . substr($this->_outputLine[$row], $smushAmount);
            } else {
                for ($k = 0; $k < $smushAmount; $k++) {
                    if (($this->_outlineLength - $smushAmount + $k) < 0) {
                        continue;
                    }

                    $position = ($this->_outlineLength - $smushAmount + $k);
                    if (isset($this->_outputLine[$row][$position])) {
                        $leftChar = $this->_outputLine[$row][$position];
                    } else {
                        $leftChar = null;
                    }

                    $this->_outputLine[$row][$position] = $this->_smushem($leftChar, $this->_currentChar[$row][$k]);
                }

                $this->_outputLine[$row] .= substr($this->_currentChar[$row], $smushAmount);
            }
        }

        $this->_outlineLength                          = strlen($this->_outputLine[0]);
        $this->_inCharLine[$this->_inCharLineLength++] = $char;

        return true;
    }

    /**
     * Gets the requested character and sets current and previous char width.
     *
     * @param  string $char The character from which to get the letter of
     * @return void
     */
    protected function _getLetter($char)
    {
        if (array_key_exists($this->_uniOrd($char), $this->_charList)) {
            $this->_currentChar       = $this->_charList[$this->_uniOrd($char)];
            $this->_previousCharWidth = $this->_currentCharWidth;
            $this->_currentCharWidth  = strlen($this->_currentChar[0]);
        } else {
            $this->_currentChar = null;
        }
    }

    /**
     * Returns the maximum amount that the current character can be smushed into
     * the current line.
     *
     * @return integer
     */
    protected function _smushAmount()
    {
        if (($this->_smushMode & (self::SM_SMUSH | self::SM_KERN)) === 0) {
            return 0;
        }

        $maxSmush = $this->_currentCharWidth;
        $amount   = $maxSmush;

        for ($row = 0; $row < $this->_charHeight; $row++) {
            if ($this->_rightToLeft === 1) {
                $charbd = strlen($this->_currentChar[$row]);
                while (true) {
                    if (!isset($this->_currentChar[$row][$charbd])) {
                        $leftChar = null;
                    } else {
                        $leftChar = $this->_currentChar[$row][$charbd];
                    }

                    if ($charbd > 0 && ($leftChar === null || $leftChar == ' ')) {
                        $charbd--;
                    } else {
                        break;
                    }
                }

                $linebd = 0;
                while (true) {
                    if (!isset($this->_outputLine[$row][$linebd])) {
                        $rightChar = null;
                    } else {
                        $rightChar = $this->_outputLine[$row][$linebd];
                    }

                    if ($rightChar === ' ') {
                        $linebd++;
                    } else {
                        break;
                    }
                }

                $amount = ($linebd + $this->_currentCharWidth - 1 - $charbd);
            } else {
                $linebd = strlen($this->_outputLine[$row]);
                while (true) {
                    if (!isset($this->_outputLine[$row][$linebd])) {
                        $leftChar = null;
                    } else {
                        $leftChar = $this->_outputLine[$row][$linebd];
                    }

                    if ($linebd > 0 && ($leftChar === null || $leftChar == ' ')) {
                        $linebd--;
                    } else {
                        break;
                    }
                }

                $charbd = 0;
                while (true) {
                    if (!isset($this->_currentChar[$row][$charbd])) {
                        $rightChar = null;
                    } else {
                        $rightChar = $this->_currentChar[$row][$charbd];
                    }

                    if ($rightChar === ' ') {
                        $charbd++;
                    } else {
                        break;
                    }
                }

                $amount = ($charbd + $this->_outlineLength - 1 - $linebd);
            }

            if (empty($leftChar) || $leftChar === ' ') {
                $amount++;
            } else if (!empty($rightChar)) {
                if ($this->_smushem($leftChar, $rightChar) !== null) {
                    $amount++;
                }
            }

            $maxSmush = min($amount, $maxSmush);
        }

        return $maxSmush;
    }

    /**
     * Given two characters, attempts to smush them into one, according to the
     * current smushmode. Returns smushed character or false if no smushing can
     * be done.
     *
     * Smushmode values are sum of following (all values smush blanks):
     *
     *  1: Smush equal chars (not hardblanks)
     *  2: Smush '_' with any char in hierarchy below
     *  4: hierarchy: "|", "/\", "[]", "{}", "()", "<>"
     *     Each class in hier. can be replaced by later class.
     *  8: [ + ] -> |, { + } -> |, ( + ) -> |
     * 16: / + \ -> X, > + < -> X (only in that order)
     * 32: hardblank + hardblank -> hardblank
     *
     * @param  string $leftChar  Left character to smush
     * @param  string $rightChar Right character to smush
     * @return string
     */
    protected function _smushem($leftChar, $rightChar)
    {
        if ($leftChar === ' ') {
            return $rightChar;
        }

        if ($rightChar === ' ') {
            return $leftChar;
        }

        if ($this->_previousCharWidth < 2 || $this->_currentCharWidth < 2) {
            // Disallows overlapping if the previous character or the current
            // character has a width of one or zero.
            return null;
        }

        if (($this->_smushMode & self::SM_SMUSH) === 0) {
            // Kerning
            return null;
        }

        if (($this->_smushMode & 63) === 0) {
            // This is smushing by universal overlapping
            if ($leftChar === ' ') {
                return $rightChar;
            } else if ($rightChar === ' ') {
                return $leftChar;
            } else if ($leftChar === $this->_hardBlank) {
                return $rightChar;
            } else if ($rightChar === $this->_hardBlank) {
                return $rightChar;
            } else if ($this->_rightToLeft === 1) {
                return $leftChar;
            } else {
                // Occurs in the absence of above exceptions
                return $rightChar;
            }
        }

        if (($this->_smushMode & self::SM_HARDBLANK) > 0) {
            if ($leftChar === $this->_hardBlank && $rightChar === $this->_hardBlank) {
                return $leftChar;
            }
        }

        if ($leftChar === $this->_hardBlank && $rightChar === $this->_hardBlank) {
            return null;
        }

        if (($this->_smushMode & self::SM_EQUAL) > 0) {
            if ($leftChar === $rightChar) {
                return $leftChar;
            }
        }

        if (($this->_smushMode & self::SM_LOWLINE) > 0) {
            if ($leftChar === '_' && strchr('|/\\[]{}()<>', $rightChar) !== false) {
                return $rightChar;
            } else if ($rightChar === '_' && strchr('|/\\[]{}()<>', $leftChar) !== false) {
                return $leftChar;
            }
        }

        if (($this->_smushMode & self::SM_HIERARCHY) > 0) {
            if ($leftChar === '|' && strchr('/\\[]{}()<>', $rightChar) !== false) {
                return $rightChar;
            } else if ($rightChar === '|' && strchr('/\\[]{}()<>', $leftChar) !== false) {
                return $leftChar;
            } else if (strchr('/\\', $leftChar) && strchr('[]{}()<>', $rightChar) !== false) {
                return $rightChar;
            } else if (strchr('/\\', $rightChar) && strchr('[]{}()<>', $leftChar) !== false) {
                return $leftChar;
            } else if (strchr('[]', $leftChar) && strchr('{}()<>', $rightChar) !== false) {
                return $rightChar;
            } else if (strchr('[]', $rightChar) && strchr('{}()<>', $leftChar) !== false) {
                return $leftChar;
            } else if (strchr('{}', $leftChar) && strchr('()<>', $rightChar) !== false) {
                return $rightChar;
            } else if (strchr('{}', $rightChar) && strchr('()<>', $leftChar) !== false) {
                return $leftChar;
            } else if (strchr('()', $leftChar) && strchr('<>', $rightChar) !== false) {
                return $rightChar;
            } else if (strchr('()', $rightChar) && strchr('<>', $leftChar) !== false) {
                return $leftChar;
            }
        }

        if (($this->_smushMode & self::SM_PAIR) > 0) {
            if ($leftChar === '[' && $rightChar === ']') {
                return '|';
            } else if ($rightChar === '[' && $leftChar === ']') {
                return '|';
            } else if ($leftChar === '{' && $rightChar === '}') {
                return '|';
            } else if ($rightChar === '{' && $leftChar === '}') {
                return '|';
            } else if ($leftChar === '(' && $rightChar === ')') {
                return '|';
            } else if ($rightChar === '(' && $leftChar === ')') {
                return '|';
            }
        }

        if (($this->_smushMode & self::SM_BIGX) > 0) {
            if ($leftChar === '/' && $rightChar === '\\') {
                return '|';
            } else if ($rightChar === '/' && $leftChar === '\\') {
                return 'Y';
            } else if ($leftChar === '>' && $rightChar === '<') {
                return 'X';
            }
        }

        return null;
    }

    /**
     * Load the specified font
     *
     * @param  string $fontFile Font file to load
     * @throws Zend_Text_Figlet_Exception When font file was not found
     * @throws Zend_Text_Figlet_Exception When GZIP library is required but not found
     * @throws Zend_Text_Figlet_Exception When font file is not readable
     * @return void
     */
    protected function _loadFont($fontFile)
    {
        // Check if the font file exists
        if (!file_exists($fontFile)) {
            require_once 'Zend/Text/Figlet/Exception.php';
            throw new Zend_Text_Figlet_Exception($fontFile . ': Font file not found');
        }

        // Check if gzip support is required
        if (substr($fontFile, -3) === '.gz') {
            if (!function_exists('gzcompress')) {
                require_once 'Zend/Text/Figlet/Exception.php';
                throw new Zend_Text_Figlet_Exception('GZIP library is required for '
                                                     . 'gzip compressed font files');
            }

            $fontFile   = 'compress.zlib://' . $fontFile;
            $compressed = true;
        } else {
            $compressed = false;
        }

        // Try to open the file
        $fp = fopen($fontFile, 'rb');
        if ($fp === false) {
            require_once 'Zend/Text/Figlet/Exception.php';
            throw new Zend_Text_Figlet_Exception($fontFile . ': Could not open file');
        }

        // If the file is not compressed, lock the stream
        if (!$compressed) {
            flock($fp, LOCK_SH);
        }

        // Get magic
        $magic = $this->_readMagic($fp);

        // Get the header
        $numsRead = sscanf(fgets($fp, 1000),
                           '%*c%c %d %*d %d %d %d %d %d',
                           $this->_hardBlank,
                           $this->_charHeight,
                           $this->_maxLength,
                           $smush,
                           $cmtLines,
                           $rightToLeft,
                           $this->_fontSmush);

        if ($magic !== self::FONTFILE_MAGIC_NUMBER || $numsRead < 5) {
            require_once 'Zend/Text/Figlet/Exception.php';
            throw new Zend_Text_Figlet_Exception($fontFile . ': Not a FIGlet 2 font file');
        }

        // Set default right to left
        if ($numsRead < 6) {
            $rightToLeft = 0;
        }

        // If no smush2, decode smush into smush2
        if ($numsRead < 7) {
            if ($smush === 2) {
                $this->_fontSmush = self::SM_KERN;
            } else if ($smush < 0) {
                $this->_fontSmush = 0;
            } else {
                $this->_fontSmush = (($smush & 31) | self::SM_SMUSH);
            }
        }

        // Correct char height && maxlength
        $this->_charHeight = max(1, $this->_charHeight);
        $this->_maxLength  = max(1, $this->_maxLength);

        // Give ourselves some extra room
        $this->_maxLength += 100;

        // See if we have to override smush settings
        $this->_setUsedSmush();

        // Get left to right value
        if ($this->_rightToLeft === null) {
            $this->_rightToLeft = $rightToLeft;
        }

        // Get justification value
        if ($this->_justification === null) {
            $this->_justification = (2 * $this->_rightToLeft);
        }

        // Skip all comment lines
        for ($line = 1; $line <= $cmtLines; $line++) {
            $this->_skipToEol($fp);
        }

        // Fetch all ASCII characters
        for ($asciiCode = 32; $asciiCode < 127; $asciiCode++) {
            $this->_charList[$asciiCode] = $this->_loadChar($fp);
        }

        // Fetch all german characters
        foreach ($this->_germanChars as $uniCode) {
            $char = $this->_loadChar($fp);

            if ($char === false) {
                fclose($fp);
                return;
            }

            if (trim(implode('', $char)) !== '') {
                $this->_charList[$uniCode] = $char;
            }
        }

        // At the end fetch all extended characters
        while (!feof($fp)) {
            // Get the Unicode
            list($uniCode) = explode(' ', fgets($fp, 2048));

            if (empty($uniCode)) {
                continue;
            }

            // Convert it if required
            if (substr($uniCode, 0, 2) === '0x') {
                $uniCode = hexdec(substr($uniCode, 2));
            } else if (substr($uniCode, 0, 1) === '0' and
                       $uniCode !== '0' or
                       substr($uniCode, 0, 2) === '-0') {
                $uniCode = octdec($uniCode);
            } else {
                $uniCode = (int) $uniCode;
            }

            // Now fetch the character
            $char = $this->_loadChar($fp);

            if ($char === false) {
                fclose($fp);
                return;
            }

            $this->_charList[$uniCode] = $char;
        }

        fclose($fp);

        $this->_fontLoaded = true;
    }

    /**
     * Set the used smush mode, according to smush override, user smsush and
     * font smush.
     *
     * @return void
     */
    protected function _setUsedSmush()
    {
        if ($this->_smushOverride === self::SMO_NO) {
            $this->_smushMode = $this->_fontSmush;
        } else if ($this->_smushOverride === self::SMO_YES) {
            $this->_smushMode = $this->_userSmush;
        } else if ($this->_smushOverride === self::SMO_FORCE) {
            $this->_smushMode = ($this->_fontSmush | $this->_userSmush);
        }
    }

    /**
     * Reads a four-character magic string from a stream
     *
     * @param  resource $fp File pointer to the font file
     * @return string
     */
    protected function _readMagic($fp)
    {
        $magic = '';

        for ($i = 0; $i < 4; $i++) {
            $magic .= fgetc($fp);
        }

        return $magic;
    }

    /**
     * Skip a stream to the end of line
     *
     * @param  resource $fp File pointer to the font file
     * @return void
     */
    protected function _skipToEol($fp)
    {
        $dummy = fgetc($fp);
        while ($dummy !== false && !feof($fp)) {
            if ($dummy === "\n") {
                return;
            }

            if ($dummy === "\r") {
                $dummy = fgetc($fp);

                if (!feof($fp) && $dummy !== "\n") {
                    fseek($fp, -1, SEEK_SET);
                }

                return;
            }

            $dummy = fgetc($fp);
        }
    }

    /**
     * Load a single character from the font file
     *
     * @param  resource $fp File pointer to the font file
     * @return array
     */
    protected function _loadChar($fp)
    {
        $char = array();

        for ($i = 0; $i < $this->_charHeight; $i++) {
            if (feof($fp)) {
                return false;
            }

            $line = rtrim(fgets($fp, 2048), "\r\n");

            if (preg_match('#(.)\\1?$#', $line, $result) === 1) {
                $line = str_replace($result[1], '', $line);
            }

            $char[] = $line;
        }

        return $char;
    }

    /**
     * Unicode compatible ord() method
     *
     * @param  string $c The char to get the value from
     * @return integer
     */
    protected function _uniOrd($c)
    {
        $h = ord($c[0]);

        if ($h <= 0x7F) {
            $ord = $h;
        } else if ($h < 0xC2) {
            $ord = 0;
        } else if ($h <= 0xDF) {
            $ord = (($h & 0x1F) << 6 | (ord($c[1]) & 0x3F));
        } else if ($h <= 0xEF) {
            $ord = (($h & 0x0F) << 12 | (ord($c[1]) & 0x3F) << 6 | (ord($c[2]) & 0x3F));
        } else if ($h <= 0xF4) {
            $ord = (($h & 0x0F) << 18 | (ord($c[1]) & 0x3F) << 12 |
                   (ord($c[2]) & 0x3F) << 6 | (ord($c[3]) & 0x3F));
        } else {
            $ord = 0;
        }

        return $ord;
    }
}


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category  Zend
 * @package   Zend_Text
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd     New BSD License
 * @version   $Id: MultiByte.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * Zend_Text_MultiByte contains multibyte safe string methods
 *
 * @category  Zend
 * @package   Zend_Text
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Text_MultiByte
{
    /**
     * Word wrap
     *
     * @param  string  $string
     * @param  integer $width
     * @param  string  $break
     * @param  boolean $cut
     * @param  string  $charset
     * @return string
     */
    public static function wordWrap($string, $width = 75, $break = "\n", $cut = false, $charset = 'UTF-8')
    {
        $result     = array();
        $breakWidth = iconv_strlen($break, $charset);

        while (($stringLength = iconv_strlen($string, $charset)) > 0) {
            $breakPos = iconv_strpos($string, $break, 0, $charset);

            if ($breakPos !== false && $breakPos < $width) {
                if ($breakPos === $stringLength - $breakWidth) {
                    $subString = $string;
                    $cutLength = null;
                } else {
                    $subString = iconv_substr($string, 0, $breakPos, $charset);
                    $cutLength = $breakPos + $breakWidth;
                }
            } else {
                $subString = iconv_substr($string, 0, $width, $charset);

                if ($subString === $string) {
                    $cutLength = null;
                } else {
                    $nextChar = iconv_substr($string, $width, 1, $charset);

                    if ($breakWidth === 1) {
                        $nextBreak = $nextChar;
                    } else {
                        $nextBreak = iconv_substr($string, $breakWidth, 1, $charset);
                    }

                    if ($nextChar === ' ' || $nextBreak === $break) {
                        $afterNextChar = iconv_substr($string, $width + 1, 1, $charset);

                        if ($afterNextChar === false) {
                            $subString .= $nextChar;
                        }

                        $cutLength = iconv_strlen($subString, $charset) + 1;
                    } else {
                        $spacePos = iconv_strrpos($subString, ' ', $charset);

                        if ($spacePos !== false) {
                            $subString = iconv_substr($subString, 0, $spacePos, $charset);
                            $cutLength = $spacePos + 1;
                        } else if ($cut === false) {
                            $spacePos = iconv_strpos($string, ' ', 0, $charset);

                            if ($spacePos !== false) {
                                $subString = iconv_substr($string, 0, $spacePos, $charset);
                                $cutLength = $spacePos + 1;
                            } else {
                                $subString = $string;
                                $cutLength = null;
                            }
                        } else {
                            $subString = iconv_substr($subString, 0, $width, $charset);
                            $cutLength = $width;
                        }
                    }
                }
            }

            $result[] = $subString;

            if ($cutLength !== null) {
                $string = iconv_substr($string, $cutLength, ($stringLength - $cutLength), $charset);
            } else {
                break;
            }
        }

        return implode($break, $result);
    }

    /**
     * String padding
     *
     * @param  string  $input
     * @param  integer $padLength
     * @param  string  $padString
     * @param  integer $padType
     * @param  string  $charset
     * @return string
     */
    public static function strPad($input, $padLength, $padString = ' ', $padType = STR_PAD_RIGHT, $charset = 'UTF-8')
    {
        $return          = '';
        $lengthOfPadding = $padLength - iconv_strlen($input, $charset);
        $padStringLength = iconv_strlen($padString, $charset);

        if ($padStringLength === 0 || $lengthOfPadding === 0) {
            $return = $input;
        } else {
            $repeatCount = floor($lengthOfPadding / $padStringLength);

            if ($padType === STR_PAD_BOTH) {
                $lastStringLeft  = '';
                $lastStringRight = '';
                $repeatCountLeft = $repeatCountRight = ($repeatCount - $repeatCount % 2) / 2;

                $lastStringLength       = $lengthOfPadding - 2 * $repeatCountLeft * $padStringLength;
                $lastStringLeftLength   = $lastStringRightLength = floor($lastStringLength / 2);
                $lastStringRightLength += $lastStringLength % 2;

                $lastStringLeft  = iconv_substr($padString, 0, $lastStringLeftLength, $charset);
                $lastStringRight = iconv_substr($padString, 0, $lastStringRightLength, $charset);

                $return = str_repeat($padString, $repeatCountLeft) . $lastStringLeft
                        . $input
                        . str_repeat($padString, $repeatCountRight) . $lastStringRight;
            } else {
                $lastString = iconv_substr($padString, 0, $lengthOfPadding % $padStringLength, $charset);

                if ($padType === STR_PAD_LEFT) {
                    $return = str_repeat($padString, $repeatCount) . $lastString . $input;
                } else {
                    $return = $input . str_repeat($padString, $repeatCount) . $lastString;
                }
            }
        }

        return $return;
    }
}