diff --git a/stream_wrapper_example/src/StreamWrapper/FileExampleSessionStreamWrapper.php b/stream_wrapper_example/src/StreamWrapper/FileExampleSessionStreamWrapper.php index b0650a7..e8a0d58 100644 --- a/stream_wrapper_example/src/StreamWrapper/FileExampleSessionStreamWrapper.php +++ b/stream_wrapper_example/src/StreamWrapper/FileExampleSessionStreamWrapper.php @@ -13,6 +13,8 @@ use Drupal\Core\Routing\UrlGeneratorTrait; * This is just an example, as it could have horrible results if much * information were placed in the $_SESSION variable. However, it does * demonstrate both the read and write implementation of a stream wrapper. + * You should *never* do this on any website accessable on the open + * Internet. * * A "stream" is an important Unix concept for the reading and writing of * files and other devices. Reading or writing a "stream" just means that you @@ -29,7 +31,8 @@ use Drupal\Core\Routing\UrlGeneratorTrait; * because the scheme "http" is supported natively in PHP. So Drupal adds * the public:// and private:// schemes, and contrib modules can add any * scheme they want to. This example adds the session:// scheme, which allows - * reading and writing the $_SESSION['stream_wrapper_example'] key as if it were a file. + * reading and writing the $_SESSION['stream_wrapper_example'] key as if it + * were a file. * * Drupal makes use of this concept to implement custom URI types like * "private://" and "public://". To implement a stream wrapper, reading @@ -54,12 +57,17 @@ use Drupal\Core\Routing\UrlGeneratorTrait; * * 3. (Optional) If you want to be able to access your files over the web, * you need to add a route that handles, and implement hook_file_download(). - * See stream_wrapper_example.routing.yml for an example of this, and file.module - * for the hook implementation. + * See stream_wrapper_example.routing.yml for an example of this, and + * file.module for the hook implementation. * * Note that because this implementation uses simple PHP arrays ($_SESSION) * it is limited to string values, so binary files will not work correctly. - * Only text files can be used. + * Only text files can be used. Also, experienced Drupal coders will + * notice that we are violating one of Drupal's coding standards here: + * normally, you should use "camelCase" for the names of your public + * functions. We cannot do this here, since PHP itself defines the interface + * used to interact with stream wrappers. Since PHP uses names_like_this + * we are required to do the same here. * * @ingroup stream_wrapper_example */ @@ -70,6 +78,8 @@ class FileExampleSessionStreamWrapper implements StreamWrapperInterface { use UrlGeneratorTrait; /** + * A representation of an HTTP request. + * * @var RequestStack */ protected $requestStack; @@ -124,7 +134,6 @@ class FileExampleSessionStreamWrapper implements StreamWrapperInterface { return StreamWrapperInterface::NORMAL; } - /** * Constructor method. * @@ -168,7 +177,6 @@ class FileExampleSessionStreamWrapper implements StreamWrapperInterface { return t('Simulated file system using your session storage. Not for real use!'); } - /** * Implements setUri(). */ @@ -246,8 +254,8 @@ class FileExampleSessionStreamWrapper implements StreamWrapperInterface { * $_SESSION variable, so there's not much to do but to create a "path" * which is really just a key in the $_SESSION variable. So something * like 'session://one/two/three.txt' becomes - * $_SESSION['stream_wrapper_example']['one']['two']['three.txt'] and the actual path - * is "one/two/three.txt". + * $_SESSION['stream_wrapper_example']['one']['two']['three.txt'] and the + * actual path is "one/two/three.txt". * * @param string $uri * Optional URI, supplied when doing a move or rename. @@ -365,7 +373,6 @@ class FileExampleSessionStreamWrapper implements StreamWrapperInterface { return TRUE; } - /** * Change stream options. * @@ -841,6 +848,7 @@ class FileExampleSessionStreamWrapper implements StreamWrapperInterface { */ public function dir_rewinddir() { $this->directoryPointer = 0; + return TRUE; } /** diff --git a/stream_wrapper_example/src/StreamWrapper/MockSessionTrait.php b/stream_wrapper_example/src/StreamWrapper/MockSessionTrait.php index 9162a72..c49bfc9 100644 --- a/stream_wrapper_example/src/StreamWrapper/MockSessionTrait.php +++ b/stream_wrapper_example/src/StreamWrapper/MockSessionTrait.php @@ -7,16 +7,21 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Prophecy\Argument; +/** + * A trait to expose a mock session type to PHPUnit tests. + */ trait MockSessionTrait { /** - * @var array + * We'll use this array to back our mock session. * - * We'll use this to back our mock session. + * @var array */ protected $sessionStore; /** + * A representation of the HTTP request. + * * @var RequestStack|ProphecyInterface */ protected $requestStack; @@ -35,19 +40,19 @@ trait MockSessionTrait { $session ->get('stream_wrapper_example', []) - ->will(function($args) use ($test) { + ->will(function ($args) use ($test) { return $test->getSessionStore(); }); $session ->set('stream_wrapper_example', Argument::any()) - ->will(function($args) use ($test) { + ->will(function ($args) use ($test) { $test->setSessionStore($args[1]); }); $session ->remove('stream_wrapper_example') - ->will(function($args) use ($test) { + ->will(function ($args) use ($test) { $test->resetSessionStore(); }); diff --git a/stream_wrapper_example/src/StreamWrapper/SessionWrapper.php b/stream_wrapper_example/src/StreamWrapper/SessionWrapper.php index c110e3a..8b8d580 100644 --- a/stream_wrapper_example/src/StreamWrapper/SessionWrapper.php +++ b/stream_wrapper_example/src/StreamWrapper/SessionWrapper.php @@ -15,14 +15,16 @@ class SessionWrapper { const SESSION_BASE_ATTRIBUTE = 'stream_wrapper_example'; /** + * Representation of the current HTTP request. + * * @var RequestStack - * Representation of the current HTTP request. */ protected $requestStack; /** + * This is the current location in our store. + * * @var string - * This is the current location in our store. */ protected $storePath; @@ -37,7 +39,6 @@ class SessionWrapper { $this->storePath = ''; } - /** * Get a fresh session object. * @@ -52,7 +53,8 @@ class SessionWrapper { * Get whatever's in the store. * * @return array - * An associated array where scalar data represents file, and arrays represent directories. + * An associated array where scalar data represents a file, and arrays + * represent directories. */ protected function getStore() { $session = $this->getSession(); @@ -61,11 +63,14 @@ class SessionWrapper { } /** - * Since we cannot deal with references to the session, write the whole - * store back. + * Set the complete content of our session. * - * @param array $store. - * The content of the whole session data store, to replace all of the current data. + * We do this since we cannot directly probe the PHP @_SESSION variable + * via a PHP reference. We can only get or set automically. + * + * @param array $store + * The content of the whole session data store, to replace all of the + * current data. */ protected function setStore($store) { $session = $this->getSession(); @@ -78,7 +83,7 @@ class SessionWrapper { * @param string $path * Path into the store. * @param bool $is_dir - * Path will be used as a container. Otherwise, just a scalar value. + * Path will be used as a container. Otherwise, path is a scalar. * * @return array|bool * Return an array containing the "bottom" and "tip" of a directory @@ -123,9 +128,10 @@ class SessionWrapper { * The equivalent to dirname() and basename() for a path. * * @param string $path + * A file-system like path string. * * @return array - * . + * Associative array defining an interal path of our data store. . */ public function getParentPath($path) { $dirs = explode('/', $path); @@ -230,7 +236,6 @@ class SessionWrapper { // Nothing to do with $_SESSION version. } - /** * Zero out the store. */ diff --git a/stream_wrapper_example/tests/src/Kernel/StreamWrapperTest.php b/stream_wrapper_example/tests/src/Kernel/StreamWrapperTest.php index 7002f1e..6441fb3 100644 --- a/stream_wrapper_example/tests/src/Kernel/StreamWrapperTest.php +++ b/stream_wrapper_example/tests/src/Kernel/StreamWrapperTest.php @@ -7,15 +7,14 @@ use Drupal\Core\Site\Settings; use Drupal\KernelTests\KernelTestBase; use Drupal\Component\Utility\Html; use Drupal\stream_wrapper_example\StreamWrapper\MockSessionTrait; -use Drupal\Core\DependencyInjection\ContainerBuilder; /** * Test of the Session Stream Wrapper Class. * * This test covers the PHP-level (i.e., not Drupal-specific) functions of the * FileExampleSessionStreamWrapper class. It's not directly loaded here because - * it loads in background automatically as soon as the stream_wrapper_example module - * loads. + * it loads in background automatically as soon as the stream_wrapper_example + * module loads. * * The tests invoke the stream wrapper's functionality indirectly by calling * PHP's file functions. @@ -44,13 +43,13 @@ class StreamWrapperTest extends KernelTestBase { FileCacheFactory::setPrefix(Settings::getApcuPrefix('file_cache', $this->root)); parent::setUp(); // Typically if we need our tested class to get information from the system, - // we use dependency injection (DI) to get that information to the class. But - // stream wrappers are unusual. They are created automatically by PHP itself - // when it calls one of the standard file functions, and for that reason, the - // constructor functions of stream wrappers cannot be passed any arguments, - // which prevents us from using the stardard DI technique we use in Drupal 8. - // The alternative is to create a "global" container that makes our services - // available to the class, which is what we do here. + // we use dependency injection (DI) to get that information to the class. + // But stream wrappers are unusual. They are created automatically by PHP + // itself when it calls one of the standard file functions, and for that + // reason, the constructor functions of stream wrappers cannot be passed any + // arguments, which prevents us from using the stardard DI technique we use + // in Drupal 8. The alternative is to create a "global" container that makes + // our services available to the class, which is what we do here. $request_stack = $this->createSessionMock(); $this->container->set('request_stack', $request_stack); $this->container->set('file_system', \Drupal::service('file_system')); @@ -96,8 +95,6 @@ class StreamWrapperTest extends KernelTestBase { $this->assertTrue(is_file($uri), "$uri is a file."); $size = filesize($uri); - // The following fails in the original implementation; the file is larger than the data. - // $this->assertEquals($len, $size, "Size of file $uri should match the data written to it.");. $contents = file_get_contents($uri); // The example implementation calls HTML::escape() on output. We reverse it // well enough for our sample data (this code is not I18n safe).