/** * getShop uses the configuration file and * provided options to build a Shop ready for use by selenium * scripts * * @param array $options * @return a Shop instance * * $options is an array with the following keys: * - initial_state: an array that will be passed to $shop->getFixtureManager()->setupInitialState(), * it is used to set the initial state of the shop for the test * * - temporary: boolean, determines whether the shop is temporary or not. * a temporary shop is always a new shop, and will likely be destroyed at the end of the tests. * if set to false, the shop is installed to path_to_web_root. * the target path will be deleted before installation and loaded from source again if it exists * AND if the overwrite option is set to true * * - overwrite: boolean, whether to overwrite or not the target shop files * when calling getShop with temporary === false - defaults to false * * - use_cache: whether or not the shop can be cached, i.e., installed once, * initialized with correct initial_state, and then restored from a copy of the files * and a dump of the database. * This is OK in most cases, but since there is some trickery involved in doing this * (replacing a few things in the DB, .htaccess file and config files) * it is not recommended to use the option in some scenarios, such as a complicated multishop * setup. * */ public function getShop(array $options) { $inplace = getenv('PSTAF_INPLACE') === '1'; $conf = $this->getNewConfiguration(); if (isset($options['conf'])) { $conf->update($options['conf']); } $options['temporary'] = !empty($options['temporary']) && !$inplace; $options['overwrite'] = !empty($options['overwrite']); $options['use_cache'] = !empty($options['use_cache']) && !$inplace; if (!isset($options['initial_state']) || !is_array($options['initial_state'])) { $options['initial_state'] = []; } // this may become a file resource // if it is, then we must close it and unlock it once // we're done. $lock = null; // whether or not we need to dump the constructed shop // if not null, it is the path where we need to put the files // after they have been built $dump_shop_to = null; // $using_cache will be true iff a warm cache exists // so when $dump_shop_to stays null $using_cache = false; // First we determine the path to the source files that // we're gonna use to build the shop. // this is the base case $source_files_path = $conf->getAsAbsolutePath('shop.filesystem_path'); // if caching is allowed, we first check // whether the source files exist or not if ($options['use_cache'] && $options['initial_state'] !== []) { // since the source files may still be under build // we acquire a lock on a file that describes the initial state $initial_state_key = $this->getInitialStateKey($options['initial_state']); // acquire lock $lock_path = FS::join($this->getWorkingDirectory(), "pstaf.{$initial_state_key}.istate.lock"); $lock = fopen($lock_path, 'w'); if (!$lock) { throw new \Exception(sprintf('Could not create lock file %s.', $lock_path)); } flock($lock, LOCK_EX); $cache_files_path = FS::join($this->getWorkingDirectory(), "pstaf.{$initial_state_key}.istate.shop"); if (is_dir($cache_files_path)) { // we're all set, release the lock and set source files path to the cached files flock($lock, LOCK_UN); fclose($lock); $lock = null; $source_files_path = $cache_files_path; $using_cache = true; } else { // well, we're out out of luck, we're going to need to build // the cache files ourselves // so we don't release the lock just yet, we'll do it when all // is nicely written // we remember this by setting $dump_shop_to to the path where cache files // will live $dump_shop_to = $cache_files_path; $using_cache = false; } } // At this point, we know where to take the files from, i.e., // from $source_files_path // We now determine where to copy them to... $shop_name = basename($conf->getAsAbsolutePath('shop.filesystem_path')); // Temporary shop needs unique URL / folder-name if ($options['temporary']) { $suffix = '_tmpshpcpy_' . $this->getUID(); $shop_name .= $suffix; } else { $suffix = ''; } // Our shop URL $url = preg_replace('#[^/]+/?$#', $shop_name . '/', $conf->get('shop.front_office_url')); // Where the shop will live $target_files_path = FS::join($conf->getAsAbsolutePath('shop.path_to_web_root'), $shop_name); $target_same_as_source = realpath($source_files_path) === realpath($target_files_path); // We say we are doing a new installation if the target files are not there $new_install = !file_exists($target_files_path); if (!$new_install && $options['overwrite'] && !$target_same_as_source) { FS::webRmR($target_files_path, $url); $new_install = true; } // Finally put the shop files in place! if (!$target_same_as_source && $new_install) { FileManagement::copyShopFiles($source_files_path, $target_files_path); } // Update the configuration with the new values $conf->set('shop.front_office_url', $url); $conf->set('shop.filesystem_path', $target_files_path); $conf->set('shop.mysql_database', $conf->get('shop.mysql_database') . $suffix); if (!isset($options['browser'])) { // Prepare to fire up selenium $seleniumHost = SeleniumManager::getHost(); $seleniumSettings = ['host' => $seleniumHost]; // Hoorah! Build our shop $shop = new Shop($conf->get('shop'), $seleniumSettings); } else { $shop = new Shop($conf->get('shop'), null); $shop->setBrowser($options['browser']); } $shop->setOptionProvider($this->optionProvider); if ($inplace && !$new_install) { // nothing for now } elseif (!$using_cache && ($new_install || $options['overwrite'])) { $shop->getFixtureManager()->setupInitialState($options['initial_state']); if ($dump_shop_to) { $shop->getFileManager()->copyShopFilesTo($dump_shop_to); $shop->getDatabaseManager()->dumpTo(FS::join($dump_shop_to, 'pstaf.shopdb.sql')); } } elseif ($using_cache) { $dump_path = FS::join($source_files_path, 'pstaf.shopdb.sql'); if (file_exists($dump_path)) { $shop->getDatabaseManager()->loadDump($dump_path)->changeShopUrlPhysicalURI("/{$shop_name}/"); } $shop->getFileManager()->updateSettingsIncIfExists(['_DB_NAME_' => $conf->get('shop.mysql_database')])->changeHtaccessPhysicalURI("/{$shop_name}/"); } $shop->setTemporary($options['temporary'] && !$inplace); if ($lock) { flock($lock, LOCK_UN); fclose($lock); } return $shop; }
/** * Delete shop files. * * We put an auto-destruct script on the server and visit it over the web, * that way even pesky www-data owned cache files are removed. * * The alternative is to run the whole script as root, which is a known bad thing. * */ public function deleteAllFiles() { FS::webRmR($this->getShop()->getFilesystemPath(), $this->getShop()->getFrontOfficeURL()); return $this; }