/** * Rock the Symfony cache to avoid fetching the same external URL over and over * These defaults are safe and boring and way faster than bashing on other servers. * But here's a tip. If you don't have APC enabled your site is probably running very, * very slowly, so fix that. And then do this for even better speed: * * a: * feed: * cache_class: sfAPCCache * cache_options: { } * @param mixed $url * @param mixed $interval * @return mixed */ public static function fetchCachedFeed($url, $interval = 300) { $cacheClass = sfConfig::get('app_a_feed_cache_class', 'sfFileCache'); $cache = new $cacheClass(sfConfig::get('app_a_feed_cache_options', array('cache_dir' => aFiles::getWritableDataFolder(array('a_feed_cache'))))); $key = 'apostrophe:feed:' . $url; $feed = $cache->get($key, false); if ($feed === 'invalid') { return false; } else { if ($feed !== false) { // sfFeed is designed to serialize well $feed = unserialize($feed); } } if (!$feed) { try { // We now always use the fopen adapter and specify a time limit, which is configurable. // Francois' comments about fopen being slow are probably dated, the stream wrappers are // quite good in modern PHP and in any case Apostrophe uses them consistently elsewhere $options = array('adapter' => 'sfFopenAdapter', 'adapter_options' => array('timeout' => sfConfig::get('app_a_feed_timeout', 30))); $feed = sfFeedPeer::createFromWeb($url, $options); $cache->set($key, serialize($feed), $interval); } catch (Exception $e) { // Cache the fact that the feed is invalid for 60 seconds so we don't // beat the daylights out of a dead feed $cache->set($key, 'invalid', 60); return false; } } return $feed; }
/** * command.post_command * @param sfEvent $event */ public static function listenToCommandPostCommandEvent(sfEvent $event) { if (aToolkitEvents::$once) { return; } aToolkitEvents::$once = true; $task = $event->getSubject(); if ($task->getFullName() === 'project:permissions') { $writable = aFiles::getWritableDataFolder(); $task->getFilesystem()->chmod($writable, 0777); $dirFinder = sfFinder::type('dir'); $fileFinder = sfFinder::type('file'); $task->getFilesystem()->chmod($dirFinder->in($writable), 0777); $task->getFilesystem()->chmod($fileFinder->in($writable), 0666); } if ($task->getFullName() === 'cache:clear') { aAssets::clearAssetCache($task->getFilesystem()); // Clear the page cache on symfony cc if (sfConfig::get('app_a_page_cache_enabled', false)) { echo "Clearing Apostrophe page cache\n"; $cache = aCacheFilter::getCache(); $cache->clean(); } else { // Cache not enabled for this environment. Too many tasks // invoke symfony cc with no environment, so let's not print // anything needlessly worrying here } } }
public static function fetchCachedFeed($url, $interval = 300) { $cacheClass = sfConfig::get('app_a_feed_cache_class', 'sfFileCache'); $cache = new $cacheClass(sfConfig::get('app_a_feed_cache_options', array('cache_dir' => aFiles::getWritableDataFolder(array('a_feed_cache'))))); $key = 'apostrophe:feed:' . $url; $feed = $cache->get($key, false); if ($feed === 'invalid') { return false; } else { if ($feed !== false) { // sfFeed is designed to serialize well $feed = unserialize($feed); } } if (!$feed) { try { $feed = sfFeedPeer::createFromWeb($url); $cache->set($key, serialize($feed), $interval); } catch (Exception $e) { // Cache the fact that the feed is invalid for 60 seconds so we don't // beat the daylights out of a dead feed $cache->set($key, 'invalid', 60); return false; } } return $feed; }
/** * DOCUMENT ME * @return mixed */ public static function getCache() { if (aAssets::$cache) { return aAssets::$cache; } $cacheClass = sfConfig::get('app_a_less_cache_class', 'sfFileCache'); aAssets::$cache = new $cacheClass(sfConfig::get('app_a_less_cache_options', array('cache_dir' => aFiles::getWritableDataFolder(array('a_less_cache'))))); return aAssets::$cache; }
/** * DOCUMENT ME * @param mixed $args * @param mixed $options */ protected function execute($args = array(), $options = array()) { $conn = 'doctrine'; $params = sfSyncContentTools::shellDatabaseParams(sfSyncContentTools::getDatabaseParams($this->configuration, $conn)); $dataDir = aFiles::getWritableDataFolder(); $uploadDir = aFiles::getUploadFolder(); // $dataDir will be a_writable, sf_data_dir is its parent, so we can put things there and // trust that they are not crushed by the unzip $dataZip = sfConfig::get('sf_data_dir') . '/apostrophedemo-awritable.zip'; if (!copy('http://content.apostrophenow.com/uploads/apostrophedemo-awritable.zip', $dataZip)) { throw new sfException("Unable to copy http://content.apostrophenow.com/uploads/apostrophedemo-awritable.zip to {$dataZip}"); } $uploadZip = sfConfig::get('sf_data_dir') . '/apostrophedemo-uploads.zip'; if (!copy("http://content.apostrophenow.com/uploads/apostrophedemo-uploads.zip", $uploadZip)) { throw new sfException('Unable to copy http://content.apostrophenow.com/uploads/apostrophedemo-uploads.zip to $dataZip'); } $this->unzip($dataDir, $dataZip, $options); $this->unzip($uploadDir, $uploadZip, $options); // Yes, you need to have mysql to use this feature. // However you can set app_syncContent_mysql to // the path of your mysql utility if it is called something // else or not in the PATH $mysql = sfConfig::get('app_syncContent_mysql', 'mysql'); system(escapeshellarg($mysql) . " {$params} < " . escapeshellarg($dataDir . '/ademocontent.sql'), $result); if ($result != 0) { throw new sfException("mysql failed. Maybe you don't have it in your PATH"); } // Undo the little dance we did to send the demo password across without forcing the use // of a known password salt or a particular encryption scheme on the receiving site. // The demo password is no secret, but new passwords set later should be, so we don't // want to force a salt when generating the demo dump. See aSyncActions for details if ($options['verbose']) { echo "Postprocessing users\n"; } $users = Doctrine::getTable('sfGuardUser')->findAll(); foreach ($users as $user) { // If there is a salt, and no password, it's really a hint to turn the cleartext password // into a proper one and establish a salt if (!strlen($user->getPassword())) { $demoPassword = $user->getSalt(); if (strlen($demoPassword)) { $user->setSalt(''); $user->setPassword($demoPassword); $user->save(); } } } if ($options['verbose']) { echo "Content loaded.\n"; } system('./symfony apostrophe:rebuild-search-index', $result); if ($result != 0) { throw new sfException('Problem executing apostrophe:rebuild-search-index task.'); } }
public static function listenToCommandPostCommandEvent(sfEvent $event) { $task = $event->getSubject(); if ($task->getFullName() === 'project:permissions') { $writable = aFiles::getWritableDataFolder(); $task->getFilesystem()->chmod($writable, 0777); $dirFinder = sfFinder::type('dir'); $fileFinder = sfFinder::type('file'); $task->getFilesystem()->chmod($dirFinder->in($writable), 0777); $task->getFilesystem()->chmod($fileFinder->in($writable), 0666); } }
public static function loadData($test = null, $configuration = null) { self::$test = $test; self::$configuration = $configuration; // Load ALL the fixtures, including plugin fixtures, not just the app level fixtures! // ALSO: we only load the fixtures on the first functional test in a set. The rest of the time we // cheat and use a mysqldump file. There are various reasons why we cannot assume it is safe to // do this in the general case, including: modified fixtures, modified schemas, modified behaviors, // and modified Doctrine. But for consecutive runs in the same PHP invocation, we definitely // don't need to start from scratch! $root = sfConfig::get('sf_root_dir'); $writable = aFiles::getWritableDataFolder(); $cache = "{$writable}/test-fixtures-cache.sql"; $reload = false; // TODO: I should check to see if a PID still exists somewhere to figure out if this is stale if (file_exists("{$writable}/test_set_pid")) { $processes = self::getProcesses(); $pid = trim(file_get_contents("{$writable}/test_set_pid")); if (!isset($processes[$pid])) { // Stale unlink("{$writable}/test_set_pid"); if (file_exists("{$writable}/test_set_first")) { unlink("{$writable}/test_set_first"); } self::info("{$writable}/test_set_pid is stale, reloading fixtures"); $reload = true; } else { if (file_exists("{$writable}/test_set_first")) { $reload = true; self::info("first test in set, reloading fixtures"); unlink("{$writable}/test_set_first"); } else { self::info("later test in set, will not reload fixtures"); } } } else { self::info("Not part of a test set, will reload fixtures"); $reload = true; } if (!$reload) { $reload = !file_exists($cache) || filesize($cache) == 0; } $params = self::getDatabaseParams(); // Attempt at autodetect that a reload is needed. This doesn't work well enough // because of modified schemas, modified behaviors, and modified Doctrine // if (!$reload) // { // $fixtures = glob("$root/data/fixtures/*.yml"); // $plugins = sfContext::getInstance()->getConfiguration()->getPlugins(); // foreach ($plugins as $plugin) // { // $pluginFixtures = glob("$root/plugins/$plugin/data/fixtures/*.yml"); // $fixtures = array_merge($fixtures, $pluginFixtures); // } // foreach ($fixtures as $fixture) // { // if (filemtime($cache) < filemtime($fixture)) // { // $reload = true; // break; // } // } // } if ($reload) { self::info("Reloading fixtures and rebuilding model, filters and forms"); self::info("(We would rather just reload fixtures but data-load does not clean up tables if there are are no entries for those tables in the fixtures, and that leaves traces of things created in those tables by previous tests. A doctrine:data-reload task is needed.)"); system(escapeshellarg(sfConfig::get('sf_root_dir') . '/symfony') . ' doctrine:build --all --db --and-load --env=test --no-confirmation', $result); self::info("Reloaded fixtures"); if ($result !== 0) { throw new sfException("Error loading data"); } // Cache for next time $sh = 'mysqldump ' . self::shellDBParams($params) . ' > ' . escapeshellarg($cache); system($sh, $result); self::info("Cached fixtures with {$sh}"); if ($result != 0) { throw new sfException("Error dumping fixtures to cache"); } } else { self::info("Reloading fixtures data from {$cache}"); system('mysql ' . self::shellDBParams($params) . ' < ' . escapeshellarg($cache), $result); if ($result != 0) { throw new sfException("Error reloading fixtures from cache"); } } return; }
/** * DOCUMENT ME * @return mixed */ protected static function getPersistentDir() { return aFiles::getWritableDataFolder(array("persistent_uploads")); }
protected function lockTree() { $dir = aFiles::getWritableDataFolder(array('a', 'locks')); $file = "{$dir}/tree.lck"; while (true) { $this->lockfp = fopen($file, 'a'); if (!$this->lockfp) { sleep(1); } else { break; } } flock($this->lockfp, LOCK_EX); }
public static function getLuceneIndexFile(Doctrine_Table $table) { return aFiles::getWritableDataFolder(array('zend_indexes')) . DIRECTORY_SEPARATOR . $table->getOption('name') . '.' . sfConfig::get('sf_environment') . '.index'; }
/** * DOCUMENT ME * @return mixed */ private function getCache() { if ($this->cache) { return $this->cache; } $cacheClass = sfConfig::get('app_a_embed_cache_class', 'sfFileCache'); $this->cache = new $cacheClass(sfConfig::get('app_a_embed_cache_options', array('cache_dir' => aFiles::getWritableDataFolder(array('a_embed_cache'))))); return $this->cache; }
public function executeZipDemo(sfWebRequest $request) { if (!$this->get('zip_demo', false)) { throw new sfException('Zip demo feature is not enabled in properties.ini'); } $uploads = aFiles::getUploadFolder(); $uploadsDemo = "{$uploads}/apostrophedemo-uploads.zip"; file_put_contents("{$uploads}/readme.txt", "This is the Symfony uploads folder. This file is here so that zipping this folder does not\nresult in an error when it happens to be empty. Move along, nothing to see here."); $this->zip($uploadsDemo, $uploads); // Has to be in the writable folder or we won't be able to write to it in many cases $data = aFiles::getWritableDataFolder(); $dump = "{$data}/ademocontent.sql"; $params = sfSyncContentTools::shellDatabaseParams(sfSyncContentTools::getDatabaseParams(sfContext::getInstance()->getConfiguration(), 'doctrine')); // Yes, you need to have mysql and mysqldump to use this feature. // However you can set app_syncContent_mysqldump to // the path of your mysqldump utility if it is called something // else or not in the PATH $mysql = sfConfig::get('app_syncContent_mysql', 'mysql'); $mysqldump = sfConfig::get('app_syncContent_mysqldump', 'mysqldump'); $cmd = escapeshellarg($mysqldump) . " --skip-opt --add-drop-table --create-options " . "--disable-keys --extended-insert --set-charset {$params} > " . escapeshellarg($dump); system($cmd, $result); if ($result != 0) { throw new sfException("mysqldump failed"); } // You can explicitly set demo_password to an empty string to keep // the passwords in your demo unchanged $demoPassword = $this->get('demo_password', 'demo'); if (!preg_match('/^\\w+$/', $demoPassword)) { throw new sfException("demo_password must contain only alphanumeric characters and underscores"); } if ($demoPassword) { // New set of parameters for a temporary database in which we'll fix the passwords so that // the demo doesn't allow dictionary attacks on your real passwords $params = array(); $params['dbname'] = $this->get('demo_tempdb'); $params['username'] = $this->get('demo_tempuser'); $params['password'] = $this->get('demo_temppassword'); $params['host'] = $this->get('demo_temphost'); $params = sfSyncContentTools::shellDatabaseParams($params); $cmd = escapeshellarg($mysql) . ' ' . $params . ' < ' . escapeshellarg($dump); system($cmd, $result); if ($result != 0) { throw new sfException("Unable to load sql into tempdb for password alteration. Did you configure tempdb, tempuser, temppassword and temphost in properties.ini?"); } // I really ought to PDO this $cmd = escapeshellarg($mysql) . ' ' . $params; $out = popen($cmd, "w"); // If we set the salt here, everyone who starts from the demo has the same salt. // If they change their passwords to real passwords, they are still vulnerable to // dictionary attack if their databases are compromised. // That's no good, so we'll fix the passwords with a clever trick in the demo fixtures // task that imports all this. We stash the demo password (not a secret) in the salt field // as cleartext for now, and the demo fixtures task grabs that, clears the salt field and // calls setPassword, resulting in a new, robustly unique salt on the new site. fwrite($out, "UPDATE sf_guard_user SET salt = '{$demoPassword}';\n"); fwrite($out, "UPDATE sf_guard_user SET password = '';\n"); $result = pclose($out); $cmd = escapeshellarg($mysqldump) . " --skip-opt --add-drop-table --create-options " . "--disable-keys --extended-insert --set-charset {$params} > " . escapeshellarg($dump); system($cmd, $result); if ($result != 0) { throw new sfException('Second mysqldump failed after password adjustment'); } } $dataDemo = "{$uploads}/apostrophedemo-awritable.zip"; $this->zip($dataDemo, $data); unlink($dump); }
/** * Lock names must be \w+ * @param mixed $name */ public static function lock($name) { $dir = aFiles::getWritableDataFolder(array('a', 'locks')); if (!preg_match('/^\\w+$/', $name)) { throw new sfException("Lock name is empty or contains non-word characters"); } $file = "{$dir}/{$name}.lck"; while (true) { $fp = fopen($file, 'a'); if ($fp) { if (flock($fp, LOCK_EX)) { break; } } sleep(1); } flock($fp, LOCK_EX); aTools::$locks[] = $fp; }
/** * DOCUMENT ME * @return mixed */ public static function getHintCache() { $cacheClass = sfConfig::get('app_a_hint_cache_class', 'sfFileCache'); $cache = new $cacheClass(sfConfig::get('app_a_hint_cache_options', array('cache_dir' => aFiles::getWritableDataFolder(array('a_hint_cache'))))); return $cache; }