Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Io Component Proposal

Proposed Component Name Zend_Io
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Io
Proposers Sven Vollbehr
Matthew Ratzloff
Jon Whitcraft
Zend Liaison TBD
Revision 1.1 - 8 August 2009 (wiki revision: 8)

Table of Contents

1. Overview

Zend_Io is a package providing means to read/write primitive PHP types (string, integers, ...) to a character stream.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • This component will read primitive PHP types from character streams.
  • This component will write primitive PHP types into character streams.
Please note
It's possible to use regular PHP file functions to carry out operations on the same resource while it is used by Zend_Io_Reader.

4. Dependencies on Other Framework Components

  • Zend_Exception

5. Theory of Operation

6. Milestones / Tasks

  • DONE: Milestone 1: Working prototype transformed from existing code (necessary tasks: conform to Zend naming conventions, and refactor to support the new API described here).
  • Milestone 2: Unit tests exist and work.
  • Milestone 3: Initial documentation exists.
  • Milestone 4: Moved to core.

7. Class Index

  • Zend_Io_Reader
  • Zend_Io_FileReader
  • Zend_Io_StringReader
  • Zend_Io_Writer
  • Zend_Io_FileWriter
  • Zend_Io_StringWriter
  • Zend_Io_Exception

8. Use Cases

UC-1
UC-2
UC-3
UC-4
UC-5

9. Class Skeletons

Zend_Io_Reader
Zend_Io_FileReader
Zend_Io_StringReader

]]></ac:plain-text-body></ac:macro>

]]></ac:plain-text-body></ac:macro>

Labels:
io io Delete
byte byte Delete
transformation transformation Delete
reader reader Delete
writer writer Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jul 02, 2009

    <p>Wouldn't the Zend_File namespace be better for this? And why would you want to make a lot of these methods + classes final?</p>

    1. Jul 02, 2009

      <p>This is an I/O component--it deals with streams. In most languages this is under an "IO" namespace.</p>

      <p> <a class="external-link" href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/package-summary.html">http://java.sun.com/j2se/1.4.2/docs/api/java/io/package-summary.html</a><br />
      <a class="external-link" href="http://www.ruby-doc.org/core/classes/IO.html">http://www.ruby-doc.org/core/classes/IO.html</a><br />
      <a class="external-link" href="http://msdn.microsoft.com/en-us/library/system.io(VS.71).aspx">http://msdn.microsoft.com/en-us/library/system.io(VS.71).aspx</a></p>

      <p>You'll notice the Zend_Io_StringReader, also.</p>

      <p>As for the final keyword, it's not necessary, but readUInt32LE() is a very specific bit of functionality. There's only one expected output from a method like this.</p>

    2. Aug 08, 2009

      <p>We thought of this alternative naming but discarded it because that would associate the functionality to a file. This has two downsides:</p>

      <p>1. The class would immediately be considered as a wrapper to PHP file functions (as it would need to contain normal file manipulation methods as well in order to fulfill its meaning). Even though this is possible, it does not comply with the requirements of Zend Framework that explicitly disallows such classes.<br />
      2. The reading operations cannot be applied to different contexts without violating the design principles. Each class should be responsible of only one thing so having a file wrapper with stream manipulation/byte transformation methods is a violation of this principle. Also, it makes more sense to have a general stream Reader class to read an in-memory buffer, for instance.</p>

      <p>As for the final methods, these are most indispensable to have. This is a consequence of the object oriented design principle called the Open-Closed Principle (OCP). The classes are not final and you can thus extend the class and create methods of your own.</p>

  2. Jul 03, 2009

    <p>Great Idea <ac:emoticon ac:name="wink" /></p>

    <p>Adding support for Buffers like in Java would be a big win. Someone could really easy implement a protocol wit this class.</p>

    <p>Something like:</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $io = new Zend_Io($socketRessource);
    $rtmp = new My_Io_Buffer_Rtmp($io);
    $rtmpPacket = $rtmp->read();
    ]]></ac:plain-text-body></ac:macro>

    <p>With a standardized buffer (some cool interfaces) it woud be really easy implementing protocols in php (at a low level) and using it (at a high level) with the object-responses of the buffer.</p>

  3. Jul 03, 2009

    <p>I like the idea of a Zend IO class.</p>

    <p>Will the Zend_IO_Exceptions contain more information about the exact error that occurred?</p>

    <p>Also, what about the unix socket, TCP/IP UDP || TCP connections?</p>

    <p>Will you be supporting protocol stream wrappers? <a class="external-link" href="http://www.php.net/manual/en/intro.stream.php">http://www.php.net/manual/en/intro.stream.php</a></p>

    <p>How do you feel about breaking the Zend_Io into 2 parts? One part for handling the actual IO, and another part for "interpreting" the IO. For example, you have Zend_Io which does the actual I/O. And then another part for reading interpreting the content as being bits, bytes, longs, ints, strings, UTF-8 vs UTF-16. etc. </p>

    <p>-daniel</p>

    1. Aug 08, 2009

      <p>The current implementation throws an exception in the following situations:</p>

      <p>1. In the constructor if, for example, the file cannot be accessed<br />
      2. The method argument is wrong (length is negative, for example)<br />
      2. Trying to operate on a closed stream</p>

      <p>In these cases a detailed error message is provided. However, currently there are theoretical cases where an exception might not be thrown, for example if fwrite causes a warning. The file descriptor is, however, checked against whether it can be written to, so this should not normally happen. Do you feel the exceptions should contain more informative error messages? In what situations?</p>

      <p>The reader/writer classes operate a PHP file descriptor so any source will do as long as it can be opened with fopen, read with fread and written with fwrite. So yes, stream wrappers are supported. </p>

      <p>I do see your point in splitting the functionality into two. Most purely OO language libraries do it like that. It would add an extra layer of abstraction and remove the dependency to fopen/fread/fwrite functions. However, it would make the class set quite complex and we might end up being against the Zend Framework design principles as it is not aiming to be such a pure OO library. That was at least my interpretation of it. Nevertheless, I will look forward to your contribution on that!</p>

      1. Sep 29, 2009

        <p>We discussed this while the component was in development, if you recall. We ultimately decided that the added complexity yielded very little benefit.</p>

  4. Jul 07, 2009

    <p>wouldn't Zend_IO_Writer_File be more consitent with ZF naming conventions?</p>

  5. Aug 03, 2009

    <p>I like this Zend_IO class, too.</p>

    <p>But I miss some special functionality:</p>
    <ul>
    <li>Read/Write IEEE 754 (<a class="external-link" href="http://de.wikipedia.org/wiki/IEEE_754">http://de.wikipedia.org/wiki/IEEE_754</a>)</li>
    <li>Read/Write Signed|Unsigned Int 24 BE|LE</li>
    <li>Read/Write/Seek Bits</li>
    </ul>

    <p>And I would think it is better to name all signed number methods with a "S" prefix<br />
    -> e.g: readSInt32LE() instead of readInt32LE()</p>

    <p>EDIT: Similar to readUInt32LE()</p>

    1. Aug 08, 2009

      <p>I have used the pack function to carry out most of the byte transformations. The documentation of pack states that the sizes as well as the representations of float and double are machine dependent, so could not use this. I did not think this further then and forgot it later. However, it is a good point that there should be a way to read/write float and doubles. Maybe you or someone else can provide me with an implementation of these methods?</p>

      <p>As for the 24-bit integers I did not know there was a need for it. However, these methods are probably possible to add using the methods for 32-bit integers.</p>

      <p>Reading/writing/seeking bits is also an interesting observation. How do you do that in PHP? I know it is possible in C and I have tried to find a way to do this myself (would be especially usefull when decoding MPEG-1/2). Currently I have used a workaround that operates on byte level and uses a bit twiddling class to deal with the bits. Again, I am more than glad to add this should you or anyone else be able to provide me with an implementation.</p>

      <p>Also, just as a side note, there is no method for unsigned 64-bit integer as it is not possible due to the limitations of internal data types PHP uses.</p>

    2. Aug 08, 2009

      <p>The naming of the methods comes from programming languages such as c and c++ where int normally denotes a signed integer and signed keyword being optional. However, the only way to define an unsigned integer, however, is to explicitly use the keyword unsigned. I could perhaps add aliases so that readSInt32LE would call readInt32LE? How do others feel about this?</p>

      1. Sep 29, 2009

        <p>The default is always signed. This is true of many languages, including SQL, which just about every PHP developer is familiar with.</p>

    3. Oct 02, 2009

      <p>Hi Sven,</p>

      <p>I think your method readFloat have to reads IEEE754 single precision (32Bit) using</p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      unpack('f', $4byte);
      ]]></ac:plain-text-body></ac:macro>
      <p>and the method readDouble have to read IEEE754 double precision (64Bit) using</p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      unpack('d', $8byte);
      ]]></ac:plain-text-body></ac:macro>

      <p>I don't know if byte order or other machine characteristics have to be handled.</p>

  6. Jul 15, 2009

    <p>Is it possible to handle Strings by only one method ?</p>

    <p>(Unicode isn't fixed 16 Bytes long -> One character can be 8 Bytes and longer)</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    readString($length = 1, $charset = null, $order = null, $trimBom = false);
    ]]></ac:plain-text-body></ac:macro>
    <ul>
    <li>If no charset is given you can auto detect it if a BOM is available else throw exception</li>
    <li>Only Multibyte charsets need the $order and $trimBom parameter</li>
    </ul>

    1. Aug 08, 2009

      <p>Yes, you are correct. The method names are a bit misleading. The 8 and 16 refer to bits used in the leading zeros.</p>

      <p>The current implementation of readString8 reads $length bytes of characters and possibly trims any leading zeros (or other given characters). WriteString8 does the opposite, writes $length amount of characters possibly padding up to the given length with $padding.</p>

      <p>The current implementation of readString16 does pretty much the same reading $length bytes of characters and trimming leading two zeros, and determining and possibly trimming the order. WriteString16 writes the missing BOM depending on the $order, and writes $length amount of characters possibly padding up to given ((int)$length/2)*2 with $padding.</p>

      <p>The two methods only make a distinction between whether to trim or pad with a single or double character. So yes, this can be done in a single method as well if there is a parameter to denote the difference. Whether this should be the name of the character set or or something else I do not know. For instance, consider the following signatures.</p>

      <ul>
      <li>readString($length, $trim='\0', $bytes=1) and</li>
      <li>writeString($value, $length=null, $padding='\0', $bytes=1)</li>
      </ul>

      <p>It would not require any mapping between character sets and number of leading zeros. Or it could perhaps be even simpler using the following signatures.</p>

      <ul>
      <li>readString($length, $trim='\0') and</li>
      <li>writeString($value, $length=null, $padding='\0')</li>
      </ul>

      <p>Double byte strings are trimmed/padded by giving '\0\0' explicitly. Any thoughts?</p>

      <p>All that BOM handling and order determination may not be required anywhere so I removed that from these examples to further simplify these methods.</p>

  7. Aug 03, 2009

    <p>@Marc: There are different encodings. UTF-16 has fixed 16 Byte Blocks (for longer chars there's the high and low surrogate), UTF-8 has 8 byte and more <ac:emoticon ac:name="wink" /></p>

    1. Aug 03, 2009

      <p>Yes, you are right but than you have to add a method like readString4 which is the same as read(1).<br />
      -> And a UTF-8 character could be <strong>theoretically</strong> endless. Which method you use to read a UTF-8 character?</p>

      <p>I think the byte length of a string can only handle by a char set and there is no usage of the byte length within the method name.</p>

  8. Sep 29, 2009

    <p>Sven,</p>

    <p>I would strongly advise contacting <ac:link><ri:user ri:username="alexander" /></ac:link> and getting his thoughts on this component. Ideally, this would become a dependency for Zend_Pdf and Zend_Search_Lucene. I know that at least Zend_Pdf has somewhat similar functionality already.</p>

    1. Mar 25, 2010

      <p>Zend_Pdf & Zend_Search_Lucene are not the only components how would need this.<br />
      -> Zend_Amf & Zend_Serializer_Adapter_PythonPickle needs this, too!</p>

      <p>For the python pickle serializer (in my opinion) it's a big overhead if on every un-/serialize call a new instance of a stream reader/writer must create.<br />
      -> It would be great to have a simple static interface like the the following:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      $value = Zend_Io_Reader::fromInt16($bin, $order = 0);
      $bin = Zend_Io_Writer::toInt16($value, $order = 0);
      ]]></ac:plain-text-body></ac:macro>

  9. Dec 16, 2009

    <p>How to edit a stream (reading & writing) ?<br />
    Need this two objects Zend_Io_Reader & Zend_Io_Writer or is it planed to add a Zend_Io_RW ?</p>

  10. Jul 28, 2010

    <p>Why are all these methods final? Also, is there an option to simultaneously read <em>and</em> write to a stream?</p>

  11. Aug 03, 2010

    <ac:macro ac:name="note"><ac:rich-text-body><p><strong>Community Review Team Recommendation</strong></p>

    <p>The CR Team advises that this component be included in 1.11, and are happy with this proposal as-is</p></ac:rich-text-body></ac:macro>