NYCPHP Meetup

NYPHP.org

[nycphp-talk] How to invoke stream_cast method?

Gary A. Mort garyamort at gmail.com
Mon Dec 30 17:00:41 EST 2013


Short question:
The stream_cast method has thrown me for a loop.  The method does not 
take any parameters and is invoked by stream_select which has lots of 
arguements - so I don't know what I'm supposed to return for it and 
where to get the options....  I figured I'd ask here if anyone else 
knows what they are.

-----Long explanation------
I've been hacking about with Google's App Engine and Joomla.  One 
limitation I ran into is that App Engine does not allow for the PHP 
application running on the website to change/modify/add files to a 
running application.   In essence, from within a PHP application running 
on App Engine[or any other language for that matter] we only have "read" 
access to the file system.

Benefits of this are of course, security[no exploits can be used to 
modify php scripts on the website] - performance[static resources such 
as javascript, images, css, etc can be optimized for delivery] and 
caching[since the files are only modified when new code is 'deployed' 
APC does not need to check the file change dates when caching PHP code - 
the deployment process can force a flush of the cache when needed].

App Engine does allow for accessing files from Google Cloud Storage 
buckets - and they even provide a stream wrapper so any gs://bucketname 
paths can be treated like files.  You can even allow for execution of 
PHP code from Cloud Storage....but then your no longer secure from 
exploits which try to modify PHP files on the server!]

For my own custom code, it's easy enough to build paths using different 
paths.  IE when checking to include php scripts with file_exists I can 
use the local file: path - and when saving or retrieiving image file 
uploads I can use the gs: file path.   But to get someone else's code 
working that way is a non-trivial chunk of work - and it struck me that 
instead I can let PHP do things "magically".  So I created my own stream 
wrapper:https://github.com/garyamort/Stream-Morpher 
<https://github.com/garyamort/Stream-Morpher>

It can use a set of string manipulation rules to convert one path to 
another, ie
ggsm://websitepath/media/image.png would become 
gs://gmimagebucket/images/image.png and a default rule at the end can 
swap ggsm://websitepath/anythingelse to file:///appinstancepath/anythingelse

For the most part, it's been fairly obvious how to map to method calls.  
Most file modifications will only be made after a call to fopen, so 
stream_open is the primary method:
         $this->streamHandle = false;
         if ($this->processPathname($pathname, $mode))
         {
             if ($this->checkRecursiveRules($this->path, 
STREAM_URL_STAT_QUIET))
             {
                 // Path translated to itself recursively, abort, abort
                 $this->path = null;
                 return false;
             }
              $use_include_path = $options & STREAM_USE_PATH;

             $handle = fopen($this->path->truePath, $mode, 
$use_include_path);
             if (is_resource($handle))
             {
                 $this->streamHandle = $handle;
                 return true;
             }

         }
         return false;


That code converts the pathname to the "true" pathname, makes sure that 
whatever stream it morphed into is not assigned to the stream morpher 
itself[otherwise you get ugly recursion calls as it tries to keep 
morphing it over and over],  and finally simply calls fopen again to 
pass the file open on to the real processor.

One oddity I found is that the return from stream_open is irrelevant, I 
originally tried returning the handle opened however future calls to 
read and write would use my morpher object, not the handle - so I had to 
save the "true" handle in the morpher object and create a proxy call for 
every method.  Some of them, such as write, are simple:
     function stream_write($data)
     {
         return fwrite($this->streamHandle, $data);
     }


Some them are ugly due to having to check lots of flags, such as set_option:
     public function stream_set_option ( $option , $arg1 , $arg2 )
     {
         if ($option & STREAM_OPTION_BLOCKING)
         {
             return stream_set_blocking($arg1, $arg2);
         }


         if ($option & STREAM_OPTION_READ_TIMEOUT)
         {
             return stream_set_timeout($arg1, $arg2);
         }


         if ($option & STREAM_OPTION_WRITE_BUFFER)
         {
             return stream_set_write_buffer($arg1, $arg2);
         }

         return false;

     }


But stream_cast has thrown me for a loop.  The method does not take any 
parameters and is invoked by stream_select which has lots of arguements 
- so I don't know what I'm supposed to return for it and where to get 
the options....  I figured I'd ask here if anyone else knows what they are.


I'm still hacking around a bit on the driver to use it for other 
things.  For example, since the mode a file is opened with is known, it 
would be possible to use the regular file stream wrapper for files 
opened in read mode, but use an ftp wrapper for files opened in write mode.

Or for things like logfile writing, since their usually a sequence of 
lines appended to a file, modify log file paths so writes get sent to 
syslog of phperror instead.

--Gary
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.nyphp.org/pipermail/talk/attachments/20131230/dc71b2d2/attachment.html>


More information about the talk mailing list