PHP Files Reading and Writing Custom File Types - Web Development and Design | Tutorial for Java, PHP, HTML, Javascript PHP Files Reading and Writing Custom File Types - Web Development and Design | Tutorial for Java, PHP, HTML, Javascript

Breaking

Post Top Ad

Post Top Ad

Monday, July 15, 2019

PHP Files Reading and Writing Custom File Types

PHP Files 


Reading and Writing Custom File Types

Problem

You want to use PHP’s standard file access functions to provide access to data that might not be in a file. For example, you want to use file access functions to read from and write to shared memory. Or you want to process file contents when they are read before they reach PHP.

Solution

Use the PEAR Stream_SHM module, which implements a stream wrapper that reads from and writes to shared memory:

       require_once 'Stream/SHM.php';
       stream_register_wrapper('shm','Stream_SHM') or die("can't register shm");
       $shm = fopen('shm://0xabcd','c');
       fwrite($shm, "Current time is: " . time());
       fclose($shm);

Discussion

Stream wrappers handle the details of moving data back and forth between PHP and your custom location or your custom format. This class implements the methods PHP needs to access your custom data stream: opening, closing, reading, writing, and so on. A particular wrapper is registered with a particular prefix. You use that prefix when passing a filename to fopen(), include(), or any other PHP file-handling function to ensure that your wrapper is invoked.

Stream wrappers are handy for nonfile data sources, but they can also be used to preprocess file contents on their way into PHP. Mike Naberezny demonstrates a clever example of this as applied to templating. With short_open_tags turned off, printing an object instance variable in a template requires the comparatively verbose <?php echo $this->property; ?>. Mike’s solution uses a stream wrapper that allows the @ character to stand in for echo $this->.

Here’s the stream wrapper code:

       <?php
       /**
         * Stream wrapper to convert markup of mostly PHP templates into PHP prior
         * to include().
         *
         * Based in large part on the example at
         * http://www.php.net/manual/en/function.stream-wrapper-register.php
         *
         * @author Mike Naberezny (@link http://mikenaberezny.com)
         * @author Paul M. Jones (@link http://paul-m-jones.com)
         */
       class ViewStream {
              /**
                * Current stream position.
                *
                * @var int
                */
              private $pos = 0;

              /**
                * Data for streaming.
                *
                * @var string
                */
              private $data;

              /**
                * Stream stats.
                *
                * @var array
                */
              private $stat;

              /**
                * Opens the script file and converts markup.
                */
              public function stream_open($path, $mode, $options, &$opened_path) {
                
                     // get the view script source
                     $path = str_replace('view://', '', $path);
                     $this->data = file_get_contents($path);

                     /**
                       * If reading the file failed, update our local stat store
                       * to reflect the real stat of the file, then return on failure
                       */
                     if ($this->data===false) {
                          $this->stat = stat($path);
                          return false;
                     }

                     /**
                       * Convert <?= ?> to long-form <?php echo ?>
                       *
                       * We could also convert <%= like the real T_OPEN_TAG_WITH_ECHO
                       * but that's not necessary.
                       *
                       * It might be nice to also convert PHP code blocks <? ?> but
                       * let's quit while we're ahead. It's probably better to keep
                       * the <?php for larger code blocks but that's your choice. If
                       * you do go for it, explicitly check for <?xml as this will
                       * probably be the biggest headache.
                       */
                     if (! ini_get('short_open_tag')) {
                          $find = '/\<\?\= (.*)? \?>/';
                          $replace = "<?php echo \$1 ?>";
                          $this->data = preg_replace($find, $replace, $this->data);
                     }
          
                     /**
                       * Convert @$ to $this->
                       *
                       * We could make a better effort at only finding @$ between <?php ?>
                       * but that's probably not necessary as @$ doesn't occur much in the wild
                       * and there's a significant performance gain by using str_replace().
                       */
                     $this->data = str_replace('@$', '$this->', $this->data);
      
                     /**
                       * file_get_contents() won't update PHP's stat cache, so performing
                       * another stat() on it will hit the filesystem again. Since the file
                       * has been successfully read, avoid this and just fake the stat
                       * so include() is happy.
                       */
                     $this->stat = array('mode' => 0100777,
                                                         'size' => strlen($this->data));

                     return true;
              }

              /**
                * Reads from the stream.
                */
              public function stream_read($count) {
                     $ret = substr($this->data, $this->pos, $count);
                     $this->pos += strlen($ret);
                     return $ret;
              }
    
              /**
                * Tells the current position in the stream.
                */
              public function stream_tell() {
                     return $this->pos;
              }

              /**
                * Tells if we are at the end of the stream.
                */
              public function stream_eof() {
                     return $this->pos >= strlen($this->data);
              }
     
              /**
                * Stream statistics.
                */
              public function stream_stat() {
                     return $this->stat;
              }

              /**
                * Seek to a specific point in the stream.
                */
              public function stream_seek($offset, $whence) {
                     switch ($whence) {
                            case SEEK_SET:
                                   if ($offset < strlen($this->data) && $offset >= 0) {
                                   $this->pos = $offset;
                                          return true;
                                   } else {
                                          return false;
                                   }
                                   break;

                            case SEEK_CUR:
                                   if ($offset >= 0) {
                                        $this->pos += $offset;
                                        return true;
                                   } else {
                                        return false;
                                   }
                                   break;

                            case SEEK_END:
                                   if (strlen($this->data) + $offset >= 0) {
                                        $this->pos = strlen($this->data) + $offset;
                                        return true;
                                   } else {
                                        return false;
                                   }
                                   break;

                            default:
                                   return false;
                     }
              }
       }

And a sample template:

       <html> <?= @$hello ?> </html>

They work together as so:

       /** Stream wrapper */
       require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'ViewStream.php';

       /**
         * A very dumb template class just to demonstrate the concept.
         *
         * @author Mike Naberezny
         * @link http://mikenaberezny.com/archives/40
         * @link http://phpsavant.com
         */
       class IdiotSavant {
              public function __construct() {
                     if (!in_array('view', stream_get_wrappers())) {
                          stream_wrapper_register('view', 'ViewStream');
                     }
              }

              public function render($filename) {
                     include 'view://' . dirname(__FILE__) . DIRECTORY_SEPARATOR .
                     $filename . '.html';
              }
       }

       // Create a new view
       $view = new IdiotSavant();

       // Assign the variable "hello" to the scope of the view
       $view->hello = 'Hello, World!';

       // Render the view from a template. Outputs "<html> Hello, World! </html>"
       $view->render('ExampleTemplate');

The stream wrapper code is saved as ViewStream.php and the sample template is named ExampleTemplate.html.


No comments:

Post a Comment

Post Top Ad