/**
  * Constructs a new DatabaseLockBackend.
  *
  * @param \Drupal\Core\Database\Connection $database
  *   The database connection.
  */
 public function __construct(Connection $database)
 {
     // __destruct() is causing problems with garbage collections, register a
     // shutdown function instead.
     drupal_register_shutdown_function(array($this, 'releaseAll'));
     $this->database = $database;
 }
 /**
  * {@inheritdoc}
  */
 public function set(AcquiaPurgeStateItemInterface $item)
 {
     if (!$this->commit) {
         $this->commit = TRUE;
         drupal_register_shutdown_function(array($this, 'commit'));
     }
     $this->items[$item->getKey()] = $item;
     $this->buffer[$item->getKey()] = $item->get();
 }
Beispiel #3
0
 /**
  * Default implementation from actual Drupal core.
  *
  * @see Redis_Lock_BackendInterface::getLockId()
  */
 public function getLockId()
 {
     if (!isset($this->_lockId)) {
         $this->_lockId = uniqid(mt_rand(), TRUE);
         // We only register a shutdown function if a lock is used.
         drupal_register_shutdown_function('lock_release_all', $this->_lockId);
     }
     return $this->_lockId;
 }
Beispiel #4
0
 /**
  * Registers a unique function call for execution on shutdown.
  *
  * Wrapper for drupal_register_shutdown_function() that does not add the
  * function call if it already exists in the shutdown function stack.
  *
  * @param callable $callback
  *   The shutdown function to register.
  * @param ...
  *   Additional arguments to pass to the shutdown function.
  *
  * @return bool
  *   TRUE if the function was added, or FALSE if it was already in the stack.
  *
  * @see drupal_register_shutdown_function()
  */
 public static function registerUniqueShutdownFunction($callback = NULL)
 {
     $args = func_get_args();
     array_shift($args);
     $existing_callbacks = drupal_register_shutdown_function();
     foreach ($existing_callbacks as $existing_callback) {
         if ($existing_callback['callback'] === $callback && $existing_callback['arguments'] === $args) {
             return FALSE;
         }
     }
     array_unshift($args, $callback);
     call_user_func_array('drupal_register_shutdown_function', $args);
     return TRUE;
 }
Beispiel #5
0
 /**
  * {@inheritdoc}
  */
 public function __construct($cid, $bin = 'default', $expire = Cache::PERMANENT, $tags = [Bootstrap::CACHE_TAG])
 {
     $this->cid = "theme_registry:storage:{$cid}";
     $this->bin = $bin;
     $this->changed = FALSE;
     $this->expire = $expire;
     $this->tags = $tags;
     // Register the cache object to save, if it's needed, on shutdown.
     drupal_register_shutdown_function([$this, 'save']);
     // Retrieve the cached data.
     $data = ($cached = \Drupal::cache($bin)->get($this->cid)) && !empty($cached->data) ? $cached->data : [];
     // Set the data.
     $this->setMultiple($data);
     // Cache has been initialized.
     $this->initialized = TRUE;
 }
Beispiel #6
0
/**
 * Perform setup tasks for all page requests.
 *
 * This hook is run at the beginning of the page request. It is typically
 * used to set up global parameters that are needed later in the request.
 *
 * Only use this hook if your code must run even for cached page views. This
 * hook is called before the theme, modules, or most include files are loaded
 * into memory. It happens while Drupal is still in bootstrap mode.
 *
 * @see hook_init()
 */
function hook_boot()
{
    // We need user_access() in the shutdown function. Make sure it gets loaded.
    drupal_load('module', 'user');
    drupal_register_shutdown_function('devel_shutdown');
}
 /**
  * Initializes devel module requirements.
  */
 public function onRequest(GetResponseEvent $event)
 {
     if (!devel_silent()) {
         if ($this->config->get('memory')) {
             global $memory_init;
             $memory_init = memory_get_usage();
         }
         if (devel_query_enabled()) {
             Database::startLog('devel');
         }
         if ($this->account->hasPermission('access devel information')) {
             devel_set_handler(devel_get_handlers());
             // We want to include the class early so that anyone may call krumo()
             // as needed. See http://krumo.sourceforge.net/
             has_krumo();
             // See http://www.firephp.org/HQ/Install.htm
             $path = NULL;
             if (@(include_once 'fb.php') || @(include_once 'FirePHPCore/fb.php')) {
                 // FirePHPCore is in include_path. Probably a PEAR installation.
                 $path = '';
             } elseif ($this->moduleHandler->moduleExists('libraries')) {
                 // Support Libraries API - http://drupal.org/project/libraries
                 $firephp_path = libraries_get_path('FirePHPCore');
                 $firephp_path = $firephp_path ? $firephp_path . '/lib/FirePHPCore/' : '';
                 $chromephp_path = libraries_get_path('chromephp');
             } else {
                 $firephp_path = DRUPAL_ROOT . '/libraries/FirePHPCore/lib/FirePHPCore/';
                 $chromephp_path = './' . drupal_get_path('module', 'devel') . '/chromephp';
             }
             // Include FirePHP if it exists.
             if (!empty($firephp_path) && file_exists($firephp_path . 'fb.php')) {
                 include_once $firephp_path . 'fb.php';
                 include_once $firephp_path . 'FirePHP.class.php';
             }
             // Include ChromePHP if it exists.
             if (!empty($chromephp_path) && file_exists($chromephp_path .= '/ChromePhp.php')) {
                 include_once $chromephp_path;
             }
         }
     }
     if ($this->config->get('rebuild_theme')) {
         drupal_theme_rebuild();
         // Ensure that the active theme object is cleared.
         $theme_name = \Drupal::theme()->getActiveTheme()->getName();
         \Drupal::state()->delete('theme.active_theme.' . $theme_name);
         \Drupal::theme()->resetActiveTheme();
         /** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler*/
         $theme_handler = \Drupal::service('theme_handler');
         $theme_handler->refreshInfo();
         // @todo This is not needed after https://www.drupal.org/node/2330755
         $list = $theme_handler->listInfo();
         $theme_handler->addTheme($list[$theme_name]);
         if (\Drupal::service('flood')->isAllowed('devel.rebuild_theme_warning', 1)) {
             \Drupal::service('flood')->register('devel.rebuild_theme_warning');
             if (!devel_silent() && $this->account->hasPermission('access devel information')) {
                 drupal_set_message(t('The theme information is being rebuilt on every request. Remember to <a href="!url">turn off</a> this feature on production websites.', array("!url" => $this->urlGenerator->generateFromRoute('devel.admin_settings'))));
             }
         }
     }
     drupal_register_shutdown_function('devel_shutdown');
 }
 /**
  * Cleans up the test environment and restores the original environment.
  *
  * Deletes created files, database tables, and reverts environment changes.
  *
  * This method needs to be invoked for both unit and integration tests.
  *
  * @see TestBase::prepareDatabasePrefix()
  * @see TestBase::changeDatabasePrefix()
  * @see TestBase::prepareEnvironment()
  */
 private function restoreEnvironment()
 {
     // Destroy the session if one was started during the test-run.
     $_SESSION = array();
     if (PHP_SAPI !== 'cli' && session_status() === PHP_SESSION_ACTIVE) {
         session_destroy();
         $params = session_get_cookie_params();
         setcookie(session_name(), '', REQUEST_TIME - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
     }
     session_name($this->originalSessionName);
     // Reset all static variables.
     // Unsetting static variables will potentially invoke destruct methods,
     // which might call into functions that prime statics and caches again.
     // In that case, all functions are still operating on the test environment,
     // which means they may need to access its filesystem and database.
     drupal_static_reset();
     if ($this->container && $this->container->has('state') && ($state = $this->container->get('state'))) {
         $captured_emails = $state->get('system.test_mail_collector') ?: array();
         $emailCount = count($captured_emails);
         if ($emailCount) {
             $message = $emailCount == 1 ? '1 email was sent during this test.' : $emailCount . ' emails were sent during this test.';
             $this->pass($message, 'Email');
         }
     }
     // Sleep for 50ms to allow shutdown functions and terminate events to
     // complete. Further information: https://www.drupal.org/node/2194357.
     usleep(50000);
     // Remove all prefixed tables.
     $original_connection_info = Database::getConnectionInfo('simpletest_original_default');
     $original_prefix = $original_connection_info['default']['prefix']['default'];
     $test_connection_info = Database::getConnectionInfo('default');
     $test_prefix = $test_connection_info['default']['prefix']['default'];
     if ($original_prefix != $test_prefix) {
         $tables = Database::getConnection()->schema()->findTables('%');
         foreach ($tables as $table) {
             if (Database::getConnection()->schema()->dropTable($table)) {
                 unset($tables[$table]);
             }
         }
     }
     // In case a fatal error occurred that was not in the test process read the
     // log to pick up any fatal errors.
     simpletest_log_read($this->testId, $this->databasePrefix, get_class($this));
     // Restore original dependency injection container.
     $this->container = $this->originalContainer;
     \Drupal::setContainer($this->originalContainer);
     // Delete test site directory.
     file_unmanaged_delete_recursive($this->siteDirectory, array($this, 'filePreDeleteCallback'));
     // Restore original database connection.
     Database::removeConnection('default');
     Database::renameConnection('simpletest_original_default', 'default');
     // Reset all static variables.
     // All destructors of statically cached objects have been invoked above;
     // this second reset is guaranteed to reset everything to nothing.
     drupal_static_reset();
     // Restore original in-memory configuration.
     $GLOBALS['config'] = $this->originalConfig;
     $GLOBALS['conf'] = $this->originalConf;
     new Settings($this->originalSettings);
     // Restore original statics and globals.
     $GLOBALS['config_directories'] = $this->originalConfigDirectories;
     // Re-initialize original stream wrappers of the parent site.
     // This must happen after static variables have been reset and the original
     // container and $config_directories are restored, as simpletest_log_read()
     // uses the public stream wrapper to locate the error.log.
     $this->originalContainer->get('stream_wrapper_manager')->register();
     if (isset($this->originalPrefix)) {
         drupal_valid_test_ua($this->originalPrefix);
     } else {
         drupal_valid_test_ua(FALSE);
     }
     // Restore original shutdown callbacks.
     $callbacks =& drupal_register_shutdown_function();
     $callbacks = $this->originalShutdownCallbacks;
 }
Beispiel #9
0
 /**
  * {@inheritdoc}
  */
 protected function assertPostConditions()
 {
     // Execute registered Drupal shutdown functions prior to tearing down.
     // @see _drupal_shutdown_function()
     $callbacks =& drupal_register_shutdown_function();
     while ($callback = array_shift($callbacks)) {
         call_user_func_array($callback['callback'], $callback['arguments']);
     }
     // Shut down the kernel (if bootKernel() was called).
     // @see \Drupal\system\Tests\DrupalKernel\DrupalKernelTest
     if ($this->container) {
         $this->container->get('kernel')->shutdown();
     }
     // Fail in case any (new) shutdown functions exist.
     $this->assertCount(0, drupal_register_shutdown_function(), 'Unexpected Drupal shutdown callbacks exist after running shutdown functions.');
     parent::assertPostConditions();
 }
Beispiel #10
0
 /**
  * Prepares the current environment for running the test.
  *
  * Also sets up new resources for the testing environment, such as the public
  * filesystem and configuration directories.
  *
  * This method is private as it must only be called once by
  * BrowserTestBase::setUp() (multiple invocations for the same test would have
  * unpredictable consequences) and it must not be callable or overridable by
  * test classes.
  */
 protected function prepareEnvironment()
 {
     // Bootstrap Drupal so we can use Drupal's built in functions.
     $this->classLoader = (require __DIR__ . '/../../../../autoload.php');
     $request = Request::createFromGlobals();
     $kernel = TestRunnerKernel::createFromRequest($request, $this->classLoader);
     // TestRunnerKernel expects the working directory to be DRUPAL_ROOT.
     chdir(DRUPAL_ROOT);
     $kernel->prepareLegacyRequest($request);
     $this->prepareDatabasePrefix();
     $this->originalSiteDirectory = $kernel->findSitePath($request);
     // Create test directory ahead of installation so fatal errors and debug
     // information can be logged during installation process.
     file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
     // Prepare filesystem directory paths.
     $this->publicFilesDirectory = $this->siteDirectory . '/files';
     $this->privateFilesDirectory = $this->siteDirectory . '/private';
     $this->tempFilesDirectory = $this->siteDirectory . '/temp';
     $this->translationFilesDirectory = $this->siteDirectory . '/translations';
     // Ensure the configImporter is refreshed for each test.
     $this->configImporter = NULL;
     // Unregister all custom stream wrappers of the parent site.
     $wrappers = \Drupal::service('stream_wrapper_manager')->getWrappers(StreamWrapperInterface::ALL);
     foreach ($wrappers as $scheme => $info) {
         stream_wrapper_unregister($scheme);
     }
     // Reset statics.
     drupal_static_reset();
     // Ensure there is no service container.
     $this->container = NULL;
     \Drupal::unsetContainer();
     // Unset globals.
     unset($GLOBALS['config_directories']);
     unset($GLOBALS['config']);
     unset($GLOBALS['conf']);
     // Log fatal errors.
     ini_set('log_errors', 1);
     ini_set('error_log', DRUPAL_ROOT . '/' . $this->siteDirectory . '/error.log');
     // Change the database prefix.
     $this->changeDatabasePrefix();
     // After preparing the environment and changing the database prefix, we are
     // in a valid test environment.
     drupal_valid_test_ua($this->databasePrefix);
     // Reset settings.
     new Settings(array('hash_salt' => $this->databasePrefix));
     drupal_set_time_limit($this->timeLimit);
     // Save and clean the shutdown callbacks array because it is static cached
     // and will be changed by the test run. Otherwise it will contain callbacks
     // from both environments and the testing environment will try to call the
     // handlers defined by the original one.
     $callbacks =& drupal_register_shutdown_function();
     $this->originalShutdownCallbacks = $callbacks;
     $callbacks = [];
 }
 /**
  * A simple page callback which adds a register shutdown function.
  */
 public function shutdownFunctions($arg1, $arg2)
 {
     drupal_register_shutdown_function('_system_test_first_shutdown_function', $arg1, $arg2);
     // If using PHP-FPM then fastcgi_finish_request() will have been fired
     // preventing further output to the browser which means that the escaping of
     // the exception message can not be tested.
     // @see _drupal_shutdown_function()
     // @see \Drupal\system\Tests\System\ShutdownFunctionsTest
     if (function_exists('fastcgi_finish_request')) {
         return ['#markup' => 'The function fastcgi_finish_request exists when serving the request.'];
     }
     return [];
 }
 /**
  * {@inheritdoc}
  */
 public function __construct($service)
 {
     parent::__construct($service);
     drupal_register_shutdown_function(array($this, 'onShutdown'));
 }
 /**
  * Fully initializes the git clone entity after it has been created or loaded.
  *
  * @param bool $force
  *   Toggle determining whether or not to force a reinitialization.
  *
  * @return GitClone
  *   The current GitClone entity instance.
  *
  * @see GitClone::save()
  * @see EntityController::load()
  *
  * @chainable
  */
 public function init($force = FALSE)
 {
     if (!$force && isset($this->initialized)) {
         return $this;
     }
     $this->initialized = TRUE;
     // Ensure a Gitonomy repository is instantiated.
     if (!$force && !isset($this->repository)) {
         $options = _git_clone_gitonomy_options();
         if (($path = $this->getPath(FALSE)) && !empty($this->url)) {
             $git_exists = file_exists("{$path}/.git");
             if (!$git_exists && in_array($this->refType, self::$allowedRefs)) {
                 try {
                     $this->repository = Admin::init($path, FALSE, $options);
                     $this->run('remote', array('add', 'origin', $this->url));
                 } catch (\Exception $e) {
                     drupal_set_message($e->getMessage(), 'error');
                 }
             } elseif ($git_exists && in_array($this->refType, self::$allowedRefs)) {
                 $this->repository = new Repository($path, $options);
             }
         } else {
             $temp_dir = 'temporary://git_clone-' . drupal_hash_base64(REQUEST_TIME);
             if (file_prepare_directory($temp_dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
                 $temp_dir = drupal_realpath($temp_dir);
                 drupal_register_shutdown_function(function () use($temp_dir) {
                     if (file_exists($temp_dir)) {
                         file_unmanaged_delete_recursive($temp_dir);
                     }
                 });
                 try {
                     $this->repository = Admin::init($temp_dir, FALSE, $options);
                 } catch (\Exception $e) {
                     watchdog('git_clone', $e->getMessage(), WATCHDOG_ERROR);
                 }
             }
             if (!$this->repository) {
                 drupal_set_message(t('Unable to create temporary git clone repository: @directory. Please verify your system temporary directory is writable.', array('@directory' => $temp_dir)), 'error');
             }
         }
     }
     // Initialize the settings.
     $this->getSettings();
     // Initialize the refs.
     $this->getRefs($force);
     return $this;
 }
 /**
  * Initializes devel module requirements.
  */
 public function onRequest(GetResponseEvent $event)
 {
     if (!devel_silent()) {
         if ($this->config->get('memory')) {
             global $memory_init;
             $memory_init = memory_get_usage();
         }
         if (devel_query_enabled()) {
             Database::startLog('devel');
         }
         if ($this->account->hasPermission('access devel information')) {
             devel_set_handler(devel_get_handlers());
             // We want to include the class early so that anyone may call krumo()
             // as needed. See http://krumo.sourceforge.net/
             has_krumo();
             // See http://www.firephp.org/HQ/Install.htm
             $path = NULL;
             if (@(include_once 'fb.php') || @(include_once 'FirePHPCore/fb.php')) {
                 // FirePHPCore is in include_path. Probably a PEAR installation.
                 $path = '';
             } elseif ($this->moduleHandler->moduleExists('libraries')) {
                 // Support Libraries API - http://drupal.org/project/libraries
                 $firephp_path = libraries_get_path('FirePHPCore');
                 $firephp_path = $firephp_path ? $firephp_path . '/lib/FirePHPCore/' : '';
                 $chromephp_path = libraries_get_path('chromephp');
             } else {
                 $firephp_path = DRUPAL_ROOT . '/libraries/FirePHPCore/lib/FirePHPCore/';
                 $chromephp_path = './' . drupal_get_path('module', 'devel') . '/chromephp';
             }
             // Include FirePHP if it exists.
             if (!empty($firephp_path) && file_exists($firephp_path . 'fb.php')) {
                 include_once $firephp_path . 'fb.php';
                 include_once $firephp_path . 'FirePHP.class.php';
             }
             // Include ChromePHP if it exists.
             if (!empty($chromephp_path) && file_exists($chromephp_path .= '/ChromePhp.php')) {
                 include_once $chromephp_path;
             }
         }
     }
     if ($this->config->get('rebuild_theme_registry')) {
         drupal_theme_rebuild();
         if (\Drupal::service('flood')->isAllowed('devel.rebuild_registry_warning', 1)) {
             \Drupal::service('flood')->register('devel.rebuild_registry_warning');
             if (!devel_silent() && $this->account->hasPermission('access devel information')) {
                 drupal_set_message(t('The theme registry is being rebuilt on every request. Remember to <a href="!url">turn off</a> this feature on production websites.', array("!url" => url('admin/config/development/devel'))));
             }
         }
     }
     drupal_register_shutdown_function('devel_shutdown');
 }
 /**
  * Overrides SearchApiEntityDataSourceController::startTracking().
  *
  * Reverts the behavior to always use getAllItemIds(), instead of taking a
  * shortcut via "base table".
  *
  * This method will also be called when the multilingual configuration of an
  * index changes, to take care of new and/or out-dated IDs.
  */
 public function startTracking(array $indexes)
 {
     if (!$this->table) {
         return;
     }
     // We first clear the tracking table for all indexes, so we can just insert
     // all items again without any key conflicts.
     $this->stopTracking($indexes);
     $operations = array();
     // Find out number of all entities to be processed.
     foreach ($indexes as $index) {
         $entity_ids = $this->getTrackableEntityIds($index);
         $steps = ceil(count($entity_ids) / $index->options['cron_limit']);
         for ($step = 0; $step < $steps; $step++) {
             $operations[] = array('search_api_et_batch_queue_entities', array($index, $entity_ids, $step));
         }
     }
     // This might be called both from web interface as well as from drush.
     $t = drupal_is_cli() ? 'dt' : 't';
     $batch = array('title' => $t('Adding items to the index queue'), 'operations' => $operations, 'finished' => 'search_api_et_batch_queue_entities_finished', 'progress_message' => $t('Completed about @percentage% of the queueing operation.'), 'file' => drupal_get_path('module', 'search_api_et') . '/search_api_et.batch.inc');
     batch_set($batch);
     if (drupal_is_cli()) {
         // Calling drush_backend_batch_process() to start batch execution directly
         // from here doesn't work for some unknown reason, so we need to call it
         // from a shutdown function instead.
         drupal_register_shutdown_function('search_api_et_shutdown_batch_process');
     } else {
         batch_process();
     }
 }
 /**
  * Don't create test db via install, instead copy existing db.
  */
 protected function setUp()
 {
     // Copy of parent::setUp();
     global $user, $language, $conf;
     // Generate a temporary prefixed database to ensure that tests have a clean starting point.
     $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000);
     db_update('simpletest_test_id')->fields(array('last_prefix' => $this->databasePrefix))->condition('test_id', $this->testId)->execute();
     // Store necessary current values before switching to prefixed database.
     $this->originalLanguage = $language;
     $this->originalLanguageDefault = variable_get('language_default');
     $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files');
     $this->originalProfile = drupal_get_profile();
     $clean_url_original = variable_get('clean_url', 0);
     // Save and clean shutdown callbacks array because it static cached and
     // will be changed by the test run. If we don't, then it will contain
     // callbacks from both environments. So testing environment will try
     // to call handlers from original environment.
     $callbacks =& drupal_register_shutdown_function();
     $this->originalShutdownCallbacks = $callbacks;
     $callbacks = array();
     // Create test directory ahead of installation so fatal errors and debug
     // information can be logged during installation process.
     // Use temporary files directory with the same prefix as the database.
     $this->public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10);
     $this->private_files_directory = $this->public_files_directory . '/private';
     $this->temp_files_directory = $this->private_files_directory . '/temp';
     // Create the directories
     file_prepare_directory($this->public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
     file_prepare_directory($this->private_files_directory, FILE_CREATE_DIRECTORY);
     file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY);
     $this->generatedTestFiles = FALSE;
     // Log fatal errors.
     ini_set('log_errors', 1);
     ini_set('error_log', $this->public_files_directory . '/error.log');
     // Set the test information for use in other parts of Drupal.
     $test_info =& $GLOBALS['drupal_test_info'];
     $test_info['test_run_id'] = $this->databasePrefix;
     $test_info['in_child_site'] = FALSE;
     // Rebuild schema based on prefixed database and such.
     $schemas = drupal_get_schema(NULL, TRUE);
     // Create a list of prefixed source table names.
     $sources = array();
     foreach ($schemas as $name => $schema) {
         $sources[$name] = Database::getConnection()->prefixTables('{' . $name . '}');
     }
     // Clone the current connection and replace the current prefix.
     $connection_info = Database::getConnectionInfo('default');
     Database::renameConnection('default', 'simpletest_original_default');
     foreach ($connection_info as $target => $value) {
         $connection_info[$target]['prefix'] = array('default' => $value['prefix']['default'] . $this->databasePrefix);
     }
     Database::addConnectionInfo('default', 'default', $connection_info['default']);
     // Clone each table into the new database.
     foreach ($schemas as $name => $schema) {
         $this->cloneTable($name, $sources[$name], $schema);
     }
     // Log in with a clean $user.
     $this->originalUser = $user;
     drupal_save_session(FALSE);
     $user = user_load(1);
     // Set up English language.
     unset($GLOBALS['conf']['language_default']);
     $language = language_default();
     // Use the test mail class instead of the default mail handler class.
     variable_set('mail_system', array('default-system' => 'TestingMailSystem'));
     drupal_set_time_limit($this->timeLimit);
     $this->resetAll();
     $this->refreshVariables();
     $this->setup = TRUE;
 }
 /**
  * @Given I update the moderation state of :named_entity to :state
  * @Given I update the moderation state of :named_entity to :state on date :date
  * @Given :workflow_user updates the moderation state of :named_entity to :state
  * @Given :workflow_user updates the moderation state of :named_entity to :state on date :date
  *
  * Transition a Moderated Node from one state to another.
  *
  * @param String|null $user The string of the username.
  * @param String $named_entity A named entity stored in the entity store.
  * @param String $state The state that you want to transition to.
  * @param String|null $date A valid php datetime string. Supports relative dates.
  * @throws \Exception
  */
 public function transitionModerationState($workflow_user = null, $named_entity, $state, $date = null)
 {
     global $user;
     // Save the original user to set it back later
     $global_user = $user;
     $node = $this->getModerationNode($named_entity);
     $possible_states = workbench_moderation_state_labels();
     $state_key = array_search($state, $possible_states);
     if (!$state_key) {
         $possible_states = implode(", ", $possible_states);
         throw new \Exception("State '{$state}' is not available. All possible states are [{$possible_states}].");
     }
     $current_user = $workflow_user ? user_load_by_name($workflow_user) : $this->getCurrentUser();
     if (!$current_user) {
         throw new \Exception("No user is logged in.");
     }
     $my_revision = $node->workbench_moderation['my_revision'];
     $state_machine_name = array_search($state, $possible_states);
     // If node is moderated to the same state but with different time, then the moderation isn't performed but the time is updated.
     if ($my_revision->state != $state_machine_name) {
         $next_states = workbench_moderation_states_next($my_revision->state, $current_user, $node);
         if (empty($next_states)) {
             $next_states = array();
         }
         if (!isset($next_states[$state_key])) {
             $next_states = implode(", ", $next_states);
             throw new \Exception("State '{$possible_states[$state_key]}' is not available to transition to. Transitions available to user '{$current_user->name}' are [{$next_states}]");
         }
         // Change global user to the current user in order to allow
         // workflow moderation to get the right user.
         $user = $current_user;
         // This function actually updates the transition.
         workbench_moderation_moderate($node, $state_key);
         // the workbench_moderation_moderate defer some status updates on the
         // node (currently the "Publish" status) to the process shutdown. Which
         // does not work well on Behat since scenarios are run on a single drupal
         // bootstrap.
         // To work around this setup. After calling the
         // `workbench_moderation_moderate` callback we check if a call to the
         // `workbench_moderation_store` function is part of the shutdown
         // execution and run it.
         $callbacks =& drupal_register_shutdown_function();
         while (list($key, $callback) = each($callbacks)) {
             if ($callback['callback'] == 'workbench_moderation_store') {
                 call_user_func_array($callback['callback'], $callback['arguments']);
                 unset($callbacks[$key]);
             }
         }
         // Back global user to the original user. Probably an anonymous.
         $user = $global_user;
     }
     // If a specific date is requested, then updated it after the fact.
     if (isset($date)) {
         $timestamp = strtotime($date, REQUEST_TIME);
         if (!$timestamp) {
             throw new \Exception("Error creating datetime from string '{$date}'");
         }
         db_update('workbench_moderation_node_history')->fields(array('stamp' => $timestamp))->condition('nid', $node->nid, '=')->condition('vid', $node->vid, '=')->execute();
     }
 }
 /**
  * Delete created files and temporary files directory, delete the tables created by setUp(),
  * and reset the database prefix.
  */
 protected function tearDown()
 {
     global $user, $language;
     // In case a fatal error occurred that was not in the test process read the
     // log to pick up any fatal errors.
     simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE);
     $emailCount = count(variable_get('drupal_test_email_collector', array()));
     if ($emailCount) {
         $message = format_plural($emailCount, '1 e-mail was sent during this test.', '@count e-mails were sent during this test.');
         $this->pass($message, t('E-mail'));
     }
     // Delete temporary files directory.
     file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10));
     // Remove all prefixed tables (all the tables in the schema).
     $schema = drupal_get_schema(NULL, TRUE);
     foreach ($schema as $name => $table) {
         db_drop_table($name);
     }
     // Get back to the original connection.
     Database::removeConnection('default');
     Database::renameConnection('simpletest_original_default', 'default');
     // Restore original shutdown callbacks array to prevent original
     // environment of calling handlers from test run.
     $callbacks =& drupal_register_shutdown_function();
     $callbacks = $this->originalShutdownCallbacks;
     // Return the user to the original one.
     $user = $this->originalUser;
     drupal_save_session(TRUE);
     // Ensure that internal logged in variable and cURL options are reset.
     $this->loggedInUser = FALSE;
     $this->additionalCurlOptions = array();
     // Reload module list and implementations to ensure that test module hooks
     // aren't called after tests.
     module_list(TRUE);
     module_implements('', FALSE, TRUE);
     // Reset the Field API.
     field_cache_clear();
     // Rebuild caches.
     $this->refreshVariables();
     // Reset language.
     $language = $this->originalLanguage;
     if ($this->originalLanguageDefault) {
         $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault;
     }
     // Close the CURL handler.
     $this->curlClose();
 }
 /**
  * Delete created files and temporary files directory, delete the tables created by setUp(),
  * and reset the database prefix.
  */
 protected function tearDown()
 {
     global $user, $language;
     // In case a fatal error occurred that was not in the test process read the
     // log to pick up any fatal errors.
     simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE);
     $emailCount = count(variable_get('drupal_test_email_collector', array()));
     if ($emailCount) {
         $message = format_plural($emailCount, '1 e-mail was sent during this test.', '@count e-mails were sent during this test.');
         $this->pass($message, t('E-mail'));
     }
     // Delete temporary files directory.
     file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10));
     // Remove all prefixed tables.
     $tables = db_find_tables($this->databasePrefix . '%');
     $connection_info = Database::getConnectionInfo('default');
     $tables = db_find_tables($connection_info['default']['prefix']['default'] . '%');
     if (empty($tables)) {
         $this->fail('Failed to find test tables to drop.');
     }
     $prefix_length = strlen($connection_info['default']['prefix']['default']);
     foreach ($tables as $table) {
         if (db_drop_table(substr($table, $prefix_length))) {
             unset($tables[$table]);
         }
     }
     if (!empty($tables)) {
         $this->fail('Failed to drop all prefixed tables.');
     }
     // Get back to the original connection.
     Database::removeConnection('default');
     Database::renameConnection('simpletest_original_default', 'default');
     // Restore original shutdown callbacks array to prevent original
     // environment of calling handlers from test run.
     $callbacks =& drupal_register_shutdown_function();
     $callbacks = $this->originalShutdownCallbacks;
     // Return the user to the original one.
     $user = $this->originalUser;
     drupal_save_session(TRUE);
     // Ensure that internal logged in variable and cURL options are reset.
     $this->loggedInUser = FALSE;
     $this->additionalCurlOptions = array();
     // Reload module list and implementations to ensure that test module hooks
     // aren't called after tests.
     module_list(TRUE);
     module_implements('', FALSE, TRUE);
     // Reset the Field API.
     field_cache_clear();
     // Rebuild caches.
     $this->refreshVariables();
     // Reset public files directory.
     $GLOBALS['conf']['file_public_path'] = $this->originalFileDirectory;
     // Reset language.
     $language = $this->originalLanguage;
     if ($this->originalLanguageDefault) {
         $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault;
     }
     // Close the CURL handler and reset the cookies array so test classes
     // containing multiple tests are not polluted.
     $this->curlClose();
     $this->cookies = array();
 }