The Morse code stream

bool stream_wrapper_register ( string protocol, string classname)

The most basic type of stream is one you can read from, so that is what we shall be creating first. For this you will need to create test.txt, a text file with a few words in that you would like converted to Morse code, and stream1.php, where we will store our script.

As mentioned already, to create your own stream you need to define a class with a set of functions. Our class, which will be called StreamMorse, needs to also store a little information about the stream, such as the file that is open, the name of the file, and the English-to-Morse conversion information. The stream functions we will be using this time are stream_open(), stream_close(), stream_read(), and stream_eof() as that gives us enough information to open and close files, read data, and make sure we are not at the end of the file.

As each of these four are callback functions, they need to be defined in a very exact manner. Here are the prototypes you need to stick to:

bool stream_open ( string path, string mode, int options, string opened_path)

void stream_close ( void )

string stream_read ( int count)

bool stream_eof ( void )

To register our stream wrapper, stream_wrapper_register() is called with the protocol you want to register as the first parameter and the stream wrapper class as the second parameter. The protocol is what you would like the class to be named as to users - for example "http://" loads the HTTP stream handler. As our stream will handle Morse code, we will be using "morse://" so we need to specify "morse" as the first parameter to stream_wrapper_register(). The stream class will be called StreamMorse, so that is the second parameter done too.

The order of events when using our class starts when someone calls fopen() with a Morse protocol. This will call our stream_open() handler, passing in the filename as the first parameter. At this point we should create a Morse conversion array, which will hold the alphabet as keys in an array and the equivalent Morse code as the values.

When fread() is called, stream_read() and stream_eof() are called, with the important work being done in the former. To convert from English to Morse, we just look up each letter in our array and add it to the returned string. Finally, fclose() is called, which triggers stream_close() and closes the file.

Here is how that looks in PHP:

    class StreamMorse {
        public $varname;
        public $fp;
        public $morse;

        private function stream_open ($path, $mode, $options, &$opened_path) {
            // store the filename just in case
            $this->varname = substr($path, 8);
            $this->fp = fopen($this->varname, $mode);
            // our conversion array
            $this->morse = array("a" => "Dit dah ", "b" => "Dah dit dit dit ", "c" => "Dah dit dah dit ", "d" => "Dah dit dit ", "e" => "Dit ", "f" => "Dit dit dah dit ", "g" => "Dah dah dit ", "h" => "Dit dit dit dit ", "i" => "Dit dit ", "j" => "Dit dah dah dah ", "k" => "Dah dit dah ", "l" => "Dit dah dit dit ", "m" => "Dah dah ", "n" => "Dah dit ", "o" => "Dah dah dah ", "p" => "Dit dah dah dit ", "q" => "Dah dah dit dah ", "r" => "Dit dah dit ", "s" => "Dit dit dit ", "t" => "Dah ", "u" => "Dit dit dah ", "v" => "Dit dit dit dah ", "w" => "Dit dah dah ", "x" => "Dah dit dit dah ", "y" => "Dah dit dah dah ", "z" => "Dah dah dit dit ", "0" => "Dah dah dah dah dah ", "1" => "Dit dah dah dah dah ", "2" => "Dit dit dah dah dah ", "3" => "Dit dit dit dah dah ", "4" => "Dit dit dit dit dah ", "5" => "Dit dit dit dit dit ", "6" => "Dah dit dit dit dit ", "7" => "Dah dah dit dit dit ", "8" => "Dah dah dah dit dit ", "9" => "Dah dah dah dah dit ", "." => "Dit dah dit dah dit dah ", "," => "Dah dah dit dit dah dah ", "?" => "Dit dit dah dah dit dit ", "@" => "Dah dit dah dit dah dit ", "\n" => "\n");
            return true;

        private function stream_read($count) {
            // read in the requested amount of text, and convert
            // to lowercase because we only have lowercase Morse
            $in = strtolower(fread($this->fp, $count));
            $inlen = strlen($in);
            $out = "";

            for ($i = 0; $i < $inlen; ++$i) {
                // skip letters we do not have a conversion for
                if (isset($this->morse[ $in{$i} ])) {
                    $out .= $this->morse[ $in{$i} ];

            return $out;

        private function stream_eof() {
            return feof($this->fp);

        private function stream_close() {
            return true;

    stream_wrapper_register("morse", "StreamMorse");
    $fp = fopen("morse://test.txt","r");
    print fread($fp, 1024);

I have scattered some comments through that to point out particularly important areas. The main work takes place in stream_read(), where a line is read in from the text file and looped through character by character. For each input character, we check whether it has an entry in the Morse conversion array, and, if it does, we add its Morse value to the output. Note that each Morse value has a capital letter at the beginning and a space at the end; this is for ease of reading. Also, "\n" has a Morse entry equal to "\n"; this is so that new lines in your text remain intact.


Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: Summary >>

Previous chapter: Custom data stream handling

Jump to:


Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.