updated core to 7.58 (right after the site was hacked)

This commit is contained in:
2018-04-20 23:48:40 +02:00
parent 18f4aba146
commit 9344a61b61
711 changed files with 99690 additions and 480 deletions

View File

@@ -0,0 +1,13 @@
name = File example
description = Examples of using the Drupal File API and Stream Wrappers.
package = Example modules
core = 7.x
files[] = file_example_session_streams.inc
files[] = file_example.test
; Information added by Drupal.org packaging script on 2017-01-10
version = "7.x-1.x-dev"
core = "7.x"
project = "examples"
datestamp = "1484076787"

View File

@@ -0,0 +1,570 @@
<?php
/**
* @file
* Examples demonstrating the Drupal File API (and Stream Wrappers).
*/
/**
* @defgroup file_example Example: Files
* @ingroup examples
* @{
* Examples demonstrating the Drupal File API (and Stream Wrappers).
*
* The File Example module is a part of the Examples for Developers Project
* and provides various Drupal File API Examples. You can download and
* experiment with this code at the
* @link http://drupal.org/project/examples Examples for Developers project page. @endlink
*
* See @link http://drupal.org/node/555118 Drupal File API @endlink for handbook
* documentation on the File API and
* @link file File summary on api.drupal.org @endlink for the function summary.
*/
/**
* Implements hook_menu().
*
* Set up the URLs (menu entries) for the file examples.
*/
function file_example_menu() {
$items = array();
$items['examples/file_example'] = array(
'title' => 'File Example',
'page callback' => 'file_example_intro',
'access callback' => TRUE,
'expanded' => TRUE,
);
$items['examples/file_example/fileapi'] = array(
'title' => 'Use File API to read/write a file',
'page callback' => 'drupal_get_form',
'access arguments' => array('use file example'),
'page arguments' => array('file_example_readwrite'),
);
$items['examples/file_example/access_session'] = array(
'page callback' => 'file_example_session_contents',
'access arguments' => array('use file example'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_permission().
*/
function file_example_permission() {
return array(
'use file example' => array(
'title' => t('Use the examples in the File Example module'),
),
);
}
/**
* A simple introduction to the workings of this module.
*/
function file_example_intro() {
$markup = t('The file example module provides a form and code to demonstrate the Drupal 7 file api. Experiment with the form, and then look at the submit handlers in the code to understand the file api.');
return array('#markup' => $markup);
}
/**
* Form builder function.
*
* A simple form that allows creation of a file, managed or unmanaged. It
* also allows reading/deleting a file and creation of a directory.
*/
function file_example_readwrite($form, &$form_state) {
if (empty($_SESSION['file_example_default_file'])) {
$_SESSION['file_example_default_file'] = 'session://drupal.txt';
}
$default_file = $_SESSION['file_example_default_file'];
if (empty($_SESSION['file_example_default_directory'])) {
$_SESSION['file_example_default_directory'] = 'session://directory1';
}
$default_directory = $_SESSION['file_example_default_directory'];
$form['write_file'] = array(
'#type' => 'fieldset',
'#title' => t('Write to a file'),
);
$form['write_file']['write_contents'] = array(
'#type' => 'textfield',
'#title' => t('Enter something you would like to write to a file') . ' ' . date('m'),
'#default_value' => t('Put some text here or just use this text'),
);
$form['write_file']['destination'] = array(
'#type' => 'textfield',
'#default_value' => $default_file,
'#title' => t('Optional: Enter the streamwrapper saying where it should be written'),
'#description' => t('This may be public://some_dir/test_file.txt or private://another_dir/some_file.txt, for example. If you include a directory, it must already exist. The default is "public://". Since this example supports session://, you can also use something like session://somefile.txt.'),
);
$form['write_file']['managed_submit'] = array(
'#type' => 'submit',
'#value' => t('Write managed file'),
'#submit' => array('file_example_managed_write_submit'),
);
$form['write_file']['unmanaged_submit'] = array(
'#type' => 'submit',
'#value' => t('Write unmanaged file'),
'#submit' => array('file_example_unmanaged_write_submit'),
);
$form['write_file']['unmanaged_php'] = array(
'#type' => 'submit',
'#value' => t('Unmanaged using PHP'),
'#submit' => array('file_example_unmanaged_php_submit'),
);
$form['fileops'] = array(
'#type' => 'fieldset',
'#title' => t('Read from a file'),
);
$form['fileops']['fileops_file'] = array(
'#type' => 'textfield',
'#default_value' => $default_file,
'#title' => t('Enter the URI of a file'),
'#description' => t('This must be a stream-type description like public://some_file.txt or http://drupal.org or private://another_file.txt or (for this example) session://yet_another_file.txt.'),
);
$form['fileops']['read_submit'] = array(
'#type' => 'submit',
'#value' => t('Read the file and store it locally'),
'#submit' => array('file_example_read_submit'),
);
$form['fileops']['delete_submit'] = array(
'#type' => 'submit',
'#value' => t('Delete file'),
'#submit' => array('file_example_delete_submit'),
);
$form['fileops']['check_submit'] = array(
'#type' => 'submit',
'#value' => t('Check to see if file exists'),
'#submit' => array('file_example_file_check_exists_submit'),
);
$form['directory'] = array(
'#type' => 'fieldset',
'#title' => t('Create or prepare a directory'),
);
$form['directory']['directory_name'] = array(
'#type' => 'textfield',
'#title' => t('Directory to create/prepare/delete'),
'#default_value' => $default_directory,
'#description' => t('This is a directory as in public://some/directory or private://another/dir.'),
);
$form['directory']['create_directory'] = array(
'#type' => 'submit',
'#value' => t('Create directory'),
'#submit' => array('file_example_create_directory_submit'),
);
$form['directory']['delete_directory'] = array(
'#type' => 'submit',
'#value' => t('Delete directory'),
'#submit' => array('file_example_delete_directory_submit'),
);
$form['directory']['check_directory'] = array(
'#type' => 'submit',
'#value' => t('Check to see if directory exists'),
'#submit' => array('file_example_check_directory_submit'),
);
$form['debug'] = array(
'#type' => 'fieldset',
'#title' => t('Debugging'),
);
$form['debug']['show_raw_session'] = array(
'#type' => 'submit',
'#value' => t('Show raw $_SESSION contents'),
'#submit' => array('file_example_show_session_contents_submit'),
);
return $form;
}
/**
* Submit handler to write a managed file.
*
* The key functions used here are:
* - file_save_data(), which takes a buffer and saves it to a named file and
* also creates a tracking record in the database and returns a file object.
* In this function we use FILE_EXISTS_RENAME (the default) as the argument,
* which means that if there's an existing file, create a new non-colliding
* filename and use it.
* - file_create_url(), which converts a URI in the form public://junk.txt or
* private://something/test.txt into a URL like
* http://example.com/sites/default/files/junk.txt.
*/
function file_example_managed_write_submit($form, &$form_state) {
$data = $form_state['values']['write_contents'];
$uri = !empty($form_state['values']['destination']) ? $form_state['values']['destination'] : NULL;
// Managed operations work with a file object.
$file_object = file_save_data($data, $uri, FILE_EXISTS_RENAME);
if (!empty($file_object)) {
$url = file_create_url($file_object->uri);
$_SESSION['file_example_default_file'] = $file_object->uri;
drupal_set_message(
t('Saved managed file: %file to destination %destination (accessible via !url, actual uri=<span id="uri">@uri</span>)',
array(
'%file' => print_r($file_object, TRUE),
'%destination' => $uri, '@uri' => $file_object->uri,
'!url' => l(t('this URL'), $url),
)
)
);
}
else {
drupal_set_message(t('Failed to save the managed file'), 'error');
}
}
/**
* Submit handler to write an unmanaged file.
*
* The key functions used here are:
* - file_unmanaged_save_data(), which takes a buffer and saves it to a named
* file, but does not create any kind of tracking record in the database.
* This example uses FILE_EXISTS_REPLACE for the third argument, meaning
* that if there's an existing file at this location, it should be replaced.
* - file_create_url(), which converts a URI in the form public://junk.txt or
* private://something/test.txt into a URL like
* http://example.com/sites/default/files/junk.txt.
*/
function file_example_unmanaged_write_submit($form, &$form_state) {
$data = $form_state['values']['write_contents'];
$destination = !empty($form_state['values']['destination']) ? $form_state['values']['destination'] : NULL;
// With the unmanaged file we just get a filename back.
$filename = file_unmanaged_save_data($data, $destination, FILE_EXISTS_REPLACE);
if ($filename) {
$url = file_create_url($filename);
$_SESSION['file_example_default_file'] = $filename;
drupal_set_message(
t('Saved file as %filename (accessible via !url, uri=<span id="uri">@uri</span>)',
array(
'%filename' => $filename,
'@uri' => $filename,
'!url' => l(t('this URL'), $url),
)
)
);
}
else {
drupal_set_message(t('Failed to save the file'), 'error');
}
}
/**
* Submit handler to write an unmanaged file using plain PHP functions.
*
* The key functions used here are:
* - file_unmanaged_save_data(), which takes a buffer and saves it to a named
* file, but does not create any kind of tracking record in the database.
* - file_create_url(), which converts a URI in the form public://junk.txt or
* private://something/test.txt into a URL like
* http://example.com/sites/default/files/junk.txt.
* - drupal_tempnam() generates a temporary filename for use.
*/
function file_example_unmanaged_php_submit($form, &$form_state) {
$data = $form_state['values']['write_contents'];
$destination = !empty($form_state['values']['destination']) ? $form_state['values']['destination'] : NULL;
if (empty($destination)) {
// If no destination has been provided, use a generated name.
$destination = drupal_tempnam('public://', 'file');
}
// With all traditional PHP functions we can use the stream wrapper notation
// for a file as well.
$fp = fopen($destination, 'w');
// To demonstrate the fact that everything is based on streams, we'll do
// multiple 5-character writes to put this to the file. We could easily
// (and far more conveniently) write it in a single statement with
// fwrite($fp, $data).
$length = strlen($data);
$write_size = 5;
for ($i = 0; $i < $length; $i += $write_size) {
$result = fwrite($fp, substr($data, $i, $write_size));
if ($result === FALSE) {
drupal_set_message(t('Failed writing to the file %file', array('%file' => $destination)), 'error');
fclose($fp);
return;
}
}
$url = file_create_url($destination);
$_SESSION['file_example_default_file'] = $destination;
drupal_set_message(
t('Saved file as %filename (accessible via !url, uri=<span id="uri">@uri</span>)',
array(
'%filename' => $destination,
'@uri' => $destination,
'!url' => l(t('this URL'), $url),
)
)
);
}
/**
* Submit handler for reading a stream wrapper.
*
* Drupal now has full support for PHP's stream wrappers, which means that
* instead of the traditional use of all the file functions
* ($fp = fopen("/tmp/some_file.txt");) far more sophisticated and generalized
* (and extensible) things can be opened as if they were files. Drupal itself
* provides the public:// and private:// schemes for handling public and
* private files. PHP provides file:// (the default) and http://, so that a
* URL can be read or written (as in a POST) as if it were a file. In addition,
* new schemes can be provided for custom applications, as will be demonstrated
* below.
*
* Here we take the stream wrapper provided in the form. We grab the
* contents with file_get_contents(). Notice that's it's as simple as that:
* file_get_contents("http://example.com") or
* file_get_contents("public://somefile.txt") just works. Although it's
* not necessary, we use file_unmanaged_save_data() to save this file locally
* and then find a local URL for it by using file_create_url().
*/
function file_example_read_submit($form, &$form_state) {
$uri = $form_state['values']['fileops_file'];
if (!is_file($uri)) {
drupal_set_message(t('The file %uri does not exist', array('%uri' => $uri)), 'error');
return;
}
// Make a working filename to save this by stripping off the (possible)
// file portion of the streamwrapper. If it's an evil file extension,
// file_munge_filename() will neuter it.
$filename = file_munge_filename(preg_replace('@^.*/@', '', $uri), '', TRUE);
$buffer = file_get_contents($uri);
if ($buffer) {
$sourcename = file_unmanaged_save_data($buffer, 'public://' . $filename);
if ($sourcename) {
$url = file_create_url($sourcename);
$_SESSION['file_example_default_file'] = $sourcename;
drupal_set_message(
t('The file was read and copied to %filename which is accessible at !url',
array(
'%filename' => $sourcename,
'!url' => l($url, $url),
)
)
);
}
else {
drupal_set_message(t('Failed to save the file'));
}
}
else {
// We failed to get the contents of the requested file.
drupal_set_message(t('Failed to retrieve the file %file', array('%file' => $uri)));
}
}
/**
* Submit handler to delete a file.
*/
function file_example_delete_submit($form, &$form_state) {
$uri = $form_state['values']['fileops_file'];
// Since we don't know if the file is managed or not, look in the database
// to see. Normally, code would be working with either managed or unmanaged
// files, so this is not a typical situation.
$file_object = file_example_get_managed_file($uri);
// If a managed file, use file_delete().
if (!empty($file_object)) {
$result = file_delete($file_object);
if ($result !== TRUE) {
drupal_set_message(t('Failed deleting managed file %uri. Result was %result',
array(
'%uri' => $uri,
'%result' => print_r($result, TRUE),
)
), 'error');
}
else {
drupal_set_message(t('Successfully deleted managed file %uri', array('%uri' => $uri)));
$_SESSION['file_example_default_file'] = $uri;
}
}
// Else use file_unmanaged_delete().
else {
$result = file_unmanaged_delete($uri);
if ($result !== TRUE) {
drupal_set_message(t('Failed deleting unmanaged file %uri', array('%uri' => $uri, 'error')));
}
else {
drupal_set_message(t('Successfully deleted unmanaged file %uri', array('%uri' => $uri)));
$_SESSION['file_example_default_file'] = $uri;
}
}
}
/**
* Submit handler to check existence of a file.
*/
function file_example_file_check_exists_submit($form, &$form_state) {
$uri = $form_state['values']['fileops_file'];
if (is_file($uri)) {
drupal_set_message(t('The file %uri exists.', array('%uri' => $uri)));
}
else {
drupal_set_message(t('The file %uri does not exist.', array('%uri' => $uri)));
}
}
/**
* Submit handler for directory creation.
*
* Here we create a directory and set proper permissions on it using
* file_prepare_directory().
*/
function file_example_create_directory_submit($form, &$form_state) {
$directory = $form_state['values']['directory_name'];
// The options passed to file_prepare_directory are a bitmask, so we can
// specify either FILE_MODIFY_PERMISSIONS (set permissions on the directory),
// FILE_CREATE_DIRECTORY, or both together:
// FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY.
// FILE_MODIFY_PERMISSIONS will set the permissions of the directory by
// by default to 0755, or to the value of the variable 'file_chmod_directory'.
if (!file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY)) {
drupal_set_message(t('Failed to create %directory.', array('%directory' => $directory)), 'error');
}
else {
drupal_set_message(t('Directory %directory is ready for use.', array('%directory' => $directory)));
$_SESSION['file_example_default_directory'] = $directory;
}
}
/**
* Submit handler for directory deletion.
*
* @see file_unmanaged_delete_recursive()
*/
function file_example_delete_directory_submit($form, &$form_state) {
$directory = $form_state['values']['directory_name'];
$result = file_unmanaged_delete_recursive($directory);
if (!$result) {
drupal_set_message(t('Failed to delete %directory.', array('%directory' => $directory)), 'error');
}
else {
drupal_set_message(t('Recursively deleted directory %directory.', array('%directory' => $directory)));
$_SESSION['file_example_default_directory'] = $directory;
}
}
/**
* Submit handler to test directory existence.
*
* This actually just checks to see if the directory is writable
*
* @param array $form
* FormAPI form.
* @param array $form_state
* FormAPI form state.
*/
function file_example_check_directory_submit($form, &$form_state) {
$directory = $form_state['values']['directory_name'];
$result = is_dir($directory);
if (!$result) {
drupal_set_message(t('Directory %directory does not exist.', array('%directory' => $directory)));
}
else {
drupal_set_message(t('Directory %directory exists.', array('%directory' => $directory)));
}
}
/**
* Utility submit function to show the contents of $_SESSION.
*/
function file_example_show_session_contents_submit($form, &$form_state) {
// If the devel module is installed, use it's nicer message format.
if (module_exists('devel')) {
dsm($_SESSION['file_example'], t('Entire $_SESSION["file_example"]'));
}
else {
drupal_set_message('<pre>' . print_r($_SESSION['file_example'], TRUE) . '</pre>');
}
}
/**
* Utility function to check for and return a managed file.
*
* In this demonstration code we don't necessarily know if a file is managed
* or not, so often need to check to do the correct behavior. Normal code
* would not have to do this, as it would be working with either managed or
* unmanaged files.
*
* @param string $uri
* The URI of the file, like public://test.txt.
*/
function file_example_get_managed_file($uri) {
$fid = db_query('SELECT fid FROM {file_managed} WHERE uri = :uri', array(':uri' => $uri))->fetchField();
if (!empty($fid)) {
$file_object = file_load($fid);
return $file_object;
}
return FALSE;
}
/**
* Implements hook_stream_wrappers().
*
* hook_stream_wrappers() is Drupal's way of exposing the class that PHP will
* use to provide a new stream wrapper class. In this case, we'll expose the
* 'session' scheme, so a file reference like "session://example/example.txt"
* is readable and writable as a location in the $_SESSION variable.
*
* @see FileExampleSessionStreamWrapper
*/
function file_example_stream_wrappers() {
$wrappers = array(
'session' => array(
'name' => t('Example: $_SESSION variable storage'),
'class' => 'FileExampleSessionStreamWrapper',
'description' => t('Store files in the $_SESSION variable as an example.'),
),
);
return $wrappers;
}
/**
* Show the contents of a session file.
*
* This page callback function is called by the Menu API for the path
* examples/file_example/access_session. Any extra path elements
* beyond this are considered to be the session path. E.g.:
* examples/file_example/access_session/foo/bar.txt would be the
* equivalent of session://foo/bar.txt, which will map into
* $_SESSION as keys: $_SESSION['foo']['bar.txt']
*
* Menu API will pass in additional path elements as function arguments. You
* can obtain these with func_get_args().
*
* @return string
* A message containing the contents of the session file.
*
* @see file_get_contents()
*/
function file_example_session_contents() {
$path_components = func_get_args();
$session_path = 'session://' . implode('/', $path_components);
$content = file_get_contents($session_path);
if ($content !== FALSE) {
return t('Contents of @path :',
array('@path' => check_plain($session_path))) . ' ' .
print_r($content, TRUE);
}
return t('Unable to load contents of: @path',
array('@path' => check_plain($session_path)));
}
/**
* @} End of "defgroup file_example".
*/

View File

@@ -0,0 +1,149 @@
<?php
/**
* @file
* Tests for File Example.
*/
/**
* Functional tests for the File Example module.
*
* @ingroup file_example
*/
class FileExampleTest extends DrupalWebTestCase {
protected $priviledgedUser;
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'File Example Functionality',
'description' => 'Test File Example features and sample streamwrapper.',
'group' => 'Examples',
);
}
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp(array('file_example'));
$this->priviledgedUser = $this->drupalCreateUser(array('use file example'));
$this->drupalLogin($this->priviledgedUser);
}
/**
* Test the basic File Example UI.
*
* - Create a directory to work with
* - Foreach scheme create and read files using each of the three methods.
*/
public function testFileExampleBasic() {
$expected_text = array(
t('Write managed file') => t('Saved managed file'),
t('Write unmanaged file') => t('Saved file as'),
t('Unmanaged using PHP') => t('Saved file as'),
);
// For each of the three buttons == three write types.
$buttons = array(
t('Write managed file'),
t('Write unmanaged file'),
t('Unmanaged using PHP'),
);
foreach ($buttons as $button) {
// For each scheme supported by Drupal + the session:// wrapper.
$schemes = array('public', 'private', 'temporary', 'session');
foreach ($schemes as $scheme) {
// Create a directory for use.
$dirname = $scheme . '://' . $this->randomName(10);
// Directory does not yet exist; assert that.
$edit = array(
'directory_name' => $dirname,
);
$this->drupalPost('examples/file_example/fileapi', $edit, t('Check to see if directory exists'));
$this->assertRaw(t('Directory %dirname does not exist', array('%dirname' => $dirname)), 'Verify that directory does not exist.');
$this->drupalPost('examples/file_example/fileapi', $edit, t('Create directory'));
$this->assertRaw(t('Directory %dirname is ready for use', array('%dirname' => $dirname)));
$this->drupalPost('examples/file_example/fileapi', $edit, t('Check to see if directory exists'));
$this->assertRaw(t('Directory %dirname exists', array('%dirname' => $dirname)), 'Verify that directory now does exist.');
// Create a file in the directory we created.
$content = $this->randomName(30);
$filename = $dirname . '/' . $this->randomName(30) . '.txt';
// Assert that the file we're about to create does not yet exist.
$edit = array(
'fileops_file' => $filename,
);
$this->drupalPost('examples/file_example/fileapi', $edit, t('Check to see if file exists'));
$this->assertRaw(t('The file %filename does not exist', array('%filename' => $filename)), 'Verify that file does not yet exist.');
debug(
t('Processing button=%button, scheme=%scheme, dir=%dirname, file=%filename',
array(
'%button' => $button,
'%scheme' => $scheme,
'%filename' => $filename,
'%dirname' => $dirname,
)
)
);
$edit = array(
'write_contents' => $content,
'destination' => $filename,
);
$this->drupalPost('examples/file_example/fileapi', $edit, $button);
$this->assertText($expected_text[$button]);
// Capture the name of the output file, as it might have changed due
// to file renaming.
$element = $this->xpath('//span[@id="uri"]');
$output_filename = (string) $element[0];
debug($output_filename, 'Name of output file');
// Click the link provided that is an easy way to get the data for
// checking and make sure that the data we put in is what we get out.
if (!in_array($scheme, array('private', 'temporary'))) {
$this->clickLink(t('this URL'));
$this->assertText($content);
}
// Verify that the file exists.
$edit = array(
'fileops_file' => $filename,
);
$this->drupalPost('examples/file_example/fileapi', $edit, t('Check to see if file exists'));
$this->assertRaw(t('The file %filename exists', array('%filename' => $filename)), 'Verify that file now exists.');
// Now read the file that got written above and verify that we can use
// the writing tools.
$edit = array(
'fileops_file' => $output_filename,
);
$this->drupalPost('examples/file_example/fileapi', $edit, t('Read the file and store it locally'));
$this->assertText(t('The file was read and copied'));
$edit = array(
'fileops_file' => $filename,
);
$this->drupalPost('examples/file_example/fileapi', $edit, t('Delete file'));
$this->assertText(t('Successfully deleted'));
$this->drupalPost('examples/file_example/fileapi', $edit, t('Check to see if file exists'));
$this->assertRaw(t('The file %filename does not exist', array('%filename' => $filename)), 'Verify file has been deleted.');
$edit = array(
'directory_name' => $dirname,
);
$this->drupalPost('examples/file_example/fileapi', $edit, t('Delete directory'));
$this->drupalPost('examples/file_example/fileapi', $edit, t('Check to see if directory exists'));
$this->assertRaw(t('Directory %dirname does not exist', array('%dirname' => $dirname)), 'Verify that directory does not exist after deletion.');
}
}
}
}

View File

@@ -0,0 +1,698 @@
<?php
/**
* @file
* Provides a demonstration session:// streamwrapper.
*
* This example is nearly fully functional, but has no known
* practical use. It's an example and demonstration only.
*/
/**
* Example stream wrapper class to handle session:// streams.
*
* 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.
*
* 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
* open some device, file, internet site, or whatever, and you don't have to
* know at all what it is. All the functions that deal with it are the same.
* You can read/write more from/to the stream, seek a position in the stream,
* or anything else without the code that does it even knowing what kind
* of device it is talking to. This Unix idea is extended into PHP's
* mindset.
*
* The idea of "stream wrapper" is that this can be extended indefinitely.
* The classic example is HTTP: With PHP you can do a
* file_get_contents("http://drupal.org/projects") as if it were a file,
* 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['file_example'] key as if it were a file.
*
* 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.
*
* @ingroup file_example
*/
class FileExampleSessionStreamWrapper implements DrupalStreamWrapperInterface {
/**
* Stream context resource.
*
* @var Resource
*/
public $context;
/**
* Instance URI (stream).
*
* These streams will be references as 'session://example_target'
*
* @var String
*/
protected $uri;
/**
* The content of the stream.
*
* Since this trivial example just uses the $_SESSION variable, this is
* simply a reference to the contents of the related part of
* $_SESSION['file_example'].
*/
protected $sessionContent;
/**
* Pointer to where we are in a directory read.
*/
protected $directoryPointer;
/**
* List of keys in a given directory.
*/
protected $directoryKeys;
/**
* The pointer to the next read or write within the session variable.
*/
protected $streamPointer;
/**
* Constructor method.
*/
public function __construct() {
$_SESSION['file_example']['.isadir.txt'] = TRUE;
}
/**
* Implements setUri().
*/
public function setUri($uri) {
$this->uri = $uri;
}
/**
* Implements getUri().
*/
public function getUri() {
return $this->uri;
}
/**
* Implements getTarget().
*
* The "target" is the portion of the URI to the right of the scheme.
* So in session://example/test.txt, the target is 'example/test.txt'.
*/
public function getTarget($uri = NULL) {
if (!isset($uri)) {
$uri = $this->uri;
}
list($scheme, $target) = explode('://', $uri, 2);
// Remove erroneous leading or trailing, forward-slashes and backslashes.
// In the session:// scheme, there is never a leading slash on the target.
return trim($target, '\/');
}
/**
* Implements getMimeType().
*/
public static function getMimeType($uri, $mapping = NULL) {
if (!isset($mapping)) {
// The default file map, defined in file.mimetypes.inc is quite big.
// We only load it when necessary.
include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
$mapping = file_mimetype_mapping();
}
$extension = '';
$file_parts = explode('.', basename($uri));
// Remove the first part: a full filename should not match an extension.
array_shift($file_parts);
// Iterate over the file parts, trying to find a match.
// For my.awesome.image.jpeg, we try:
// - jpeg
// - image.jpeg, and
// - awesome.image.jpeg
while ($additional_part = array_pop($file_parts)) {
$extension = drupal_strtolower($additional_part . ($extension ? '.' . $extension : ''));
if (isset($mapping['extensions'][$extension])) {
return $mapping['mimetypes'][$mapping['extensions'][$extension]];
}
}
return 'application/octet-stream';
}
/**
* Implements getDirectoryPath().
*
* In this case there is no directory string, so return an empty string.
*/
public function getDirectoryPath() {
return '';
}
/**
* Overrides getExternalUrl().
*
* We have set up a helper function and menu entry to provide access to this
* key via HTTP; normally it would be accessible some other way.
*/
public function getExternalUrl() {
$path = $this->getLocalPath();
$url = url('examples/file_example/access_session/' . $path, array('absolute' => TRUE));
return $url;
}
/**
* We have no concept of chmod, so just return TRUE.
*/
public function chmod($mode) {
return TRUE;
}
/**
* Implements realpath().
*/
public function realpath() {
return 'session://' . $this->getLocalPath();
}
/**
* Returns the local path.
*
* Here we aren't doing anything but stashing the "file" in a key in the
* $_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['file_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.
*/
protected function getLocalPath($uri = NULL) {
if (!isset($uri)) {
$uri = $this->uri;
}
$path = str_replace('session://', '', $uri);
$path = trim($path, '/');
return $path;
}
/**
* Opens a stream, as for fopen(), file_get_contents(), file_put_contents().
*
* @param string $uri
* A string containing the URI to the file to open.
* @param string $mode
* The file mode ("r", "wb" etc.).
* @param int $options
* A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
* @param string &$opened_path
* A string containing the path actually opened.
*
* @return bool
* Returns TRUE if file was opened successfully. (Always returns TRUE).
*
* @see http://php.net/manual/en/streamwrapper.stream-open.php
*/
public function stream_open($uri, $mode, $options, &$opened_path) {
$this->uri = $uri;
// We make $session_content a reference to the appropriate key in the
// $_SESSION variable. So if the local path were
// /example/test.txt it $session_content would now be a
// reference to $_SESSION['file_example']['example']['test.txt'].
$this->sessionContent = &$this->uri_to_session_key($uri);
// Reset the stream pointer since this is an open.
$this->streamPointer = 0;
return TRUE;
}
/**
* Return a reference to the correct $_SESSION key.
*
* @param string $uri
* The uri: session://something
* @param bool $create
* If TRUE, create the key
*
* @return array|bool
* A reference to the array at the end of the key-path, or
* FALSE if the path doesn't map to a key-path (and $create is FALSE).
*/
protected function &uri_to_session_key($uri, $create = TRUE) {
// Since our uri_to_session_key() method returns a reference, we
// have to set up a failure flag variable.
$fail = FALSE;
$path = $this->getLocalPath($uri);
$path_components = explode('/', $path);
// Set up a reference to the root session:// 'directory.'
$var = &$_SESSION['file_example'];
// Handle case of just session://.
if (count($path_components) < 1) {
return $var;
}
// Walk through the path components and create keys in $_SESSION,
// unless we're told not to create them.
foreach ($path_components as $component) {
if ($create || isset($var[$component])) {
$var = &$var[$component];
}
else {
// This path doesn't exist as keys, either because the
// key doesn't exist, or because we're told not to create it.
return $fail;
}
}
return $var;
}
/**
* Support for flock().
*
* The $_SESSION variable has no locking capability, so return TRUE.
*
* @param int $operation
* One of the following:
* - LOCK_SH to acquire a shared lock (reader).
* - LOCK_EX to acquire an exclusive lock (writer).
* - LOCK_UN to release a lock (shared or exclusive).
* - LOCK_NB if you don't want flock() to block while locking (not
* supported on Windows).
*
* @return bool
* Always returns TRUE at the present time. (no support)
*
* @see http://php.net/manual/en/streamwrapper.stream-lock.php
*/
public function stream_lock($operation) {
return TRUE;
}
/**
* Support for fread(), file_get_contents() etc.
*
* @param int $count
* Maximum number of bytes to be read.
*
* @return string
* The string that was read, or FALSE in case of an error.
*
* @see http://php.net/manual/en/streamwrapper.stream-read.php
*/
public function stream_read($count) {
if (is_string($this->sessionContent)) {
$remaining_chars = drupal_strlen($this->sessionContent) - $this->streamPointer;
$number_to_read = min($count, $remaining_chars);
if ($remaining_chars > 0) {
$buffer = drupal_substr($this->sessionContent, $this->streamPointer, $number_to_read);
$this->streamPointer += $number_to_read;
return $buffer;
}
}
return FALSE;
}
/**
* Support for fwrite(), file_put_contents() etc.
*
* @param string $data
* The string to be written.
*
* @return int
* The number of bytes written (integer).
*
* @see http://php.net/manual/en/streamwrapper.stream-write.php
*/
public function stream_write($data) {
// Sanitize the data in a simple way since we're putting it into the
// session variable.
$data = check_plain($data);
$this->sessionContent = substr_replace($this->sessionContent, $data, $this->streamPointer);
$this->streamPointer += drupal_strlen($data);
return drupal_strlen($data);
}
/**
* Support for feof().
*
* @return bool
* TRUE if end-of-file has been reached.
*
* @see http://php.net/manual/en/streamwrapper.stream-eof.php
*/
public function stream_eof() {
return FALSE;
}
/**
* Support for fseek().
*
* @param int $offset
* The byte offset to got to.
* @param int $whence
* SEEK_SET, SEEK_CUR, or SEEK_END.
*
* @return bool
* TRUE on success.
*
* @see http://php.net/manual/en/streamwrapper.stream-seek.php
*/
public function stream_seek($offset, $whence) {
if (drupal_strlen($this->sessionContent) >= $offset) {
$this->streamPointer = $offset;
return TRUE;
}
return FALSE;
}
/**
* Support for fflush().
*
* @return bool
* TRUE if data was successfully stored (or there was no data to store).
* This always returns TRUE, as this example provides and needs no
* flush support.
*
* @see http://php.net/manual/en/streamwrapper.stream-flush.php
*/
public function stream_flush() {
return TRUE;
}
/**
* Support for ftell().
*
* @return int
* The current offset in bytes from the beginning of file.
*
* @see http://php.net/manual/en/streamwrapper.stream-tell.php
*/
public function stream_tell() {
return $this->streamPointer;
}
/**
* Support for fstat().
*
* @return array
* An array with file status, or FALSE in case of an error - see fstat()
* for a description of this array.
*
* @see http://php.net/manual/en/streamwrapper.stream-stat.php
*/
public function stream_stat() {
return array(
'size' => drupal_strlen($this->sessionContent),
);
}
/**
* Support for fclose().
*
* @return bool
* TRUE if stream was successfully closed.
*
* @see http://php.net/manual/en/streamwrapper.stream-close.php
*/
public function stream_close() {
$this->streamPointer = 0;
// Unassign the reference.
unset($this->sessionContent);
return TRUE;
}
/**
* Support for unlink().
*
* @param string $uri
* A string containing the uri to the resource to delete.
*
* @return bool
* TRUE if resource was successfully deleted.
*
* @see http://php.net/manual/en/streamwrapper.unlink.php
*/
public function unlink($uri) {
$path = $this->getLocalPath($uri);
$path_components = preg_split('/\//', $path);
$unset = '$_SESSION[\'file_example\']';
foreach ($path_components as $component) {
$unset .= '[\'' . $component . '\']';
}
// TODO: Is there a better way to delete from an array?
// drupal_array_get_nested_value() doesn't work because it only returns
// a reference; unsetting a reference only unsets the reference.
eval("unset($unset);");
return TRUE;
}
/**
* Support for rename().
*
* @param string $from_uri
* The uri to the file to rename.
* @param string $to_uri
* The new uri for file.
*
* @return bool
* TRUE if file was successfully renamed.
*
* @see http://php.net/manual/en/streamwrapper.rename.php
*/
public function rename($from_uri, $to_uri) {
$from_key = &$this->uri_to_session_key($from_uri);
$to_key = &$this->uri_to_session_key($to_uri);
if (is_dir($to_key) || is_file($to_key)) {
return FALSE;
}
$to_key = $from_key;
unset($from_key);
return TRUE;
}
/**
* Gets the name of the directory from a given path.
*
* @param string $uri
* A URI.
*
* @return string
* A string containing the directory name.
*
* @see drupal_dirname()
*/
public function dirname($uri = NULL) {
list($scheme, $target) = explode('://', $uri, 2);
$target = $this->getTarget($uri);
if (strpos($target, '/')) {
$dirname = preg_replace('@/[^/]*$@', '', $target);
}
else {
$dirname = '';
}
return $scheme . '://' . $dirname;
}
/**
* Support for mkdir().
*
* @param string $uri
* A string containing the URI to the directory to create.
* @param int $mode
* Permission flags - see mkdir().
* @param int $options
* A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
*
* @return bool
* TRUE if directory was successfully created.
*
* @see http://php.net/manual/en/streamwrapper.mkdir.php
*/
public function mkdir($uri, $mode, $options) {
// If this already exists, then we can't mkdir.
if (is_dir($uri) || is_file($uri)) {
return FALSE;
}
// Create the key in $_SESSION;
$this->uri_to_session_key($uri, TRUE);
// Place a magic file inside it to differentiate this from an empty file.
$marker_uri = $uri . '/.isadir.txt';
$this->uri_to_session_key($marker_uri, TRUE);
return TRUE;
}
/**
* Support for rmdir().
*
* @param string $uri
* A string containing the URI to the directory to delete.
* @param int $options
* A bit mask of STREAM_REPORT_ERRORS.
*
* @return bool
* TRUE if directory was successfully removed.
*
* @see http://php.net/manual/en/streamwrapper.rmdir.php
*/
public function rmdir($uri, $options) {
$path = $this->getLocalPath($uri);
$path_components = preg_split('/\//', $path);
$unset = '$_SESSION[\'file_example\']';
foreach ($path_components as $component) {
$unset .= '[\'' . $component . '\']';
}
// TODO: I really don't like this eval.
debug($unset, 'array element to be unset');
eval("unset($unset);");
return TRUE;
}
/**
* Support for stat().
*
* This important function goes back to the Unix way of doing things.
* In this example almost the entire stat array is irrelevant, but the
* mode is very important. It tells PHP whether we have a file or a
* directory and what the permissions are. All that is packed up in a
* bitmask. This is not normal PHP fodder.
*
* @param string $uri
* A string containing the URI to get information about.
* @param int $flags
* A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
*
* @return array|bool
* An array with file status, or FALSE in case of an error - see fstat()
* for a description of this array.
*
* @see http://php.net/manual/en/streamwrapper.url-stat.php
*/
public function url_stat($uri, $flags) {
// Get a reference to the $_SESSION key for this URI.
$key = $this->uri_to_session_key($uri, FALSE);
// Default to fail.
$return = FALSE;
$mode = 0;
// We will call an array a directory and the root is always an array.
if (is_array($key) && array_key_exists('.isadir.txt', $key)) {
// S_IFDIR means it's a directory.
$mode = 0040000;
}
elseif ($key !== FALSE) {
// S_IFREG, means it's a file.
$mode = 0100000;
}
if ($mode) {
$size = 0;
if ($mode == 0100000) {
$size = drupal_strlen($key);
}
// There are no protections on this, so all writable.
$mode |= 0777;
$return = array(
'dev' => 0,
'ino' => 0,
'mode' => $mode,
'nlink' => 0,
'uid' => 0,
'gid' => 0,
'rdev' => 0,
'size' => $size,
'atime' => 0,
'mtime' => 0,
'ctime' => 0,
'blksize' => 0,
'blocks' => 0,
);
}
return $return;
}
/**
* Support for opendir().
*
* @param string $uri
* A string containing the URI to the directory to open.
* @param int $options
* Whether or not to enforce safe_mode (0x04).
*
* @return bool
* TRUE on success.
*
* @see http://php.net/manual/en/streamwrapper.dir-opendir.php
*/
public function dir_opendir($uri, $options) {
$var = &$this->uri_to_session_key($uri, FALSE);
if ($var === FALSE || !array_key_exists('.isadir.txt', $var)) {
return FALSE;
}
// We grab the list of key names, flip it so that .isadir.txt can easily
// be removed, then flip it back so we can easily walk it as a list.
$this->directoryKeys = array_flip(array_keys($var));
unset($this->directoryKeys['.isadir.txt']);
$this->directoryKeys = array_keys($this->directoryKeys);
$this->directoryPointer = 0;
return TRUE;
}
/**
* Support for readdir().
*
* @return string|bool
* The next filename, or FALSE if there are no more files in the directory.
*
* @see http://php.net/manual/en/streamwrapper.dir-readdir.php
*/
public function dir_readdir() {
if ($this->directoryPointer < count($this->directoryKeys)) {
$next = $this->directoryKeys[$this->directoryPointer];
$this->directoryPointer++;
return $next;
}
return FALSE;
}
/**
* Support for rewinddir().
*
* @return bool
* TRUE on success.
*
* @see http://php.net/manual/en/streamwrapper.dir-rewinddir.php
*/
public function dir_rewinddir() {
$this->directoryPointer = 0;
}
/**
* Support for closedir().
*
* @return bool
* TRUE on success.
*
* @see http://php.net/manual/en/streamwrapper.dir-closedir.php
*/
public function dir_closedir() {
$this->directoryPointer = 0;
unset($this->directoryKeys);
return TRUE;
}
}