private function getRawData() { if ($this->rawData === null) { $type = $this->getDataType(); $data = $this->getData(); switch ($type) { case self::DATATYPE_TEXT: // In this storage type, the changes are stored on the object. $data = $data; break; case self::DATATYPE_FILE: default: throw new Exception(pht('Hunk has unsupported data type "%s"!', $type)); } $format = $this->getDataFormat(); switch ($format) { case self::DATAFORMAT_RAW: // In this format, the changes are stored as-is. $data = $data; break; case self::DATAFORMAT_DEFLATED: $data = PhabricatorCaches::inflateData($data); break; default: throw new Exception(pht('Hunk has unsupported data encoding "%s"!', $type)); } $this->rawData = $data; } return $this->rawData; }
public static function getLiveServers() { $cache = PhabricatorCaches::getRequestCache(); $refs = $cache->getKey(self::KEY_REFS); if (!$refs) { $refs = self::newRefs(); $cache->setKey(self::KEY_REFS, $refs); } return $refs; }
protected function run() { $this->setEngines(PhabricatorFactEngine::loadAllEngines()); while (!$this->shouldExit()) { PhabricatorCaches::destroyRequestCache(); $iterators = $this->getAllApplicationIterators(); foreach ($iterators as $iterator_name => $iterator) { $this->processIteratorWithCursor($iterator_name, $iterator); } $this->processAggregates(); $this->log(pht('Zzz...')); $this->sleep(60 * 5); } }
private function runLoop() { do { PhabricatorCaches::destroyRequestCache(); $this->stillWorking(); $messages = $this->protocolAdapter->getNextMessages($this->pollFrequency); if (count($messages) > 0) { foreach ($messages as $message) { $this->routeMessage($message); } } foreach ($this->handlers as $handler) { $handler->runBackgroundTasks(); } } while (!$this->shouldExit()); }
private function getEngine() { $options = $this->options; $viewer = $this->getViewer(); $viewer_key = $viewer->getCacheFragment(); ksort($options); $engine_key = serialize($options); $engine_key = PhabricatorHash::digestForIndex($engine_key); $cache = PhabricatorCaches::getRequestCache(); $cache_key = "remarkup.engine({$viewer_key}, {$engine_key})"; $engine = $cache->getKey($cache_key); if (!$engine) { $engine = PhabricatorMarkupEngine::newMarkupEngine($options); $cache->setKey($cache_key, $engine); } return $engine; }
public static function getLiveDevice() { $device_id = self::getDeviceID(); if (!$device_id) { return null; } $cache = PhabricatorCaches::getRequestCache(); $cache_key = 'almanac.device.self'; $device = $cache->getKey($cache_key); if (!$device) { $viewer = PhabricatorUser::getOmnipotentUser(); $device = id(new AlmanacDeviceQuery())->setViewer($viewer)->withNames(array($device_id))->executeOne(); if (!$device) { throw new Exception(pht('This host has device ID "%s", but there is no corresponding ' . 'device record in Almanac.', $device_id)); } $cache->setKey($cache_key, $device); } return $device; }
public function testRequestCache() { $cache = PhabricatorCaches::getRequestCache(); $test_key = 'unit.' . Filesystem::readRandomCharacters(8); $default_value = pht('Default'); $new_value = pht('New Value'); $this->assertEqual($default_value, $cache->getKey($test_key, $default_value)); // Set a key, verify it persists. $cache = PhabricatorCaches::getRequestCache(); $cache->setKey($test_key, $new_value); $this->assertEqual($new_value, $cache->getKey($test_key, $default_value)); // Refetch the cache, verify it's really a cache. $cache = PhabricatorCaches::getRequestCache(); $this->assertEqual($new_value, $cache->getKey($test_key, $default_value)); // Destroy the cache. PhabricatorCaches::destroyRequestCache(); // Now, the value should be missing again. $cache = PhabricatorCaches::getRequestCache(); $this->assertEqual($default_value, $cache->getKey($test_key, $default_value)); }
protected function run() { do { PhabricatorCaches::destroyRequestCache(); $tasks = id(new PhabricatorWorkerLeaseQuery())->setLimit(1)->execute(); if ($tasks) { $this->willBeginWork(); foreach ($tasks as $task) { $id = $task->getID(); $class = $task->getTaskClass(); $this->log(pht('Working on task %d (%s)...', $id, $class)); $task = $task->executeTask(); $ex = $task->getExecutionException(); if ($ex) { if ($ex instanceof PhabricatorWorkerPermanentFailureException) { throw new PhutilProxyException(pht('Permanent failure while executing Task ID %d.', $id), $ex); } else { if ($ex instanceof PhabricatorWorkerYieldException) { $this->log(pht('Task %s yielded.', $id)); } else { $this->log(pht('Task %d failed!', $id)); throw new PhutilProxyException(pht('Error while executing Task ID %d.', $id), $ex); } } } else { $this->log(pht('Task %s complete! Moved to archive.', $id)); } } $sleep = 0; } else { // When there's no work, sleep for one second. The pool will // autoscale down if we're continuously idle for an extended period // of time. $this->willBeginIdle(); $sleep = 1; } $this->sleep($sleep); } while (!$this->shouldExit()); }
/** * Determine if an application is installed and available to a viewer, by * application class name. * * To check if an application is installed at all, use * @{method:isClassInstalled}. * * @param string Application class name. * @param PhabricatorUser Viewing user. * @return bool True if the class is installed for the viewer. * @task meta */ public static final function isClassInstalledForViewer($class, PhabricatorUser $viewer) { if ($viewer->isOmnipotent()) { return true; } $cache = PhabricatorCaches::getRequestCache(); $viewer_phid = $viewer->getPHID(); $key = 'app.' . $class . '.installed.' . $viewer_phid; $result = $cache->getKey($key); if ($result === null) { if (!self::isClassInstalled($class)) { $result = false; } else { $result = PhabricatorPolicyFilter::hasCapability($viewer, self::getByClass($class), PhabricatorPolicyCapability::CAN_VIEW); } $cache->setKey($key, $result); } return $result; }
private function renderPhabricatorLogo() { $custom_header = PhabricatorCustomLogoConfigType::getLogoImagePHID(); $logo_style = array(); if ($custom_header) { $cache = PhabricatorCaches::getImmutableCache(); $cache_key_logo = 'ui.custom-header.logo-phid.v3.' . $custom_header; $logo_uri = $cache->getKey($cache_key_logo); if (!$logo_uri) { $file = id(new PhabricatorFileQuery())->setViewer($this->getViewer())->withPHIDs(array($custom_header))->executeOne(); if ($file) { $logo_uri = $file->getViewURI(); $cache->setKey($cache_key_logo, $logo_uri); } } $logo_style[] = 'background-size: 40px 40px;'; $logo_style[] = 'background-position: 0 0;'; $logo_style[] = 'background-image: url(' . $logo_uri . ')'; } $logo_node = phutil_tag('span', array('class' => 'phabricator-main-menu-eye', 'style' => implode(' ', $logo_style))); $wordmark_text = PhabricatorCustomLogoConfigType::getLogoWordmark(); if (!strlen($wordmark_text)) { $wordmark_text = pht('Phabricator'); } $wordmark_node = phutil_tag('span', array('class' => 'phabricator-wordmark'), $wordmark_text); return phutil_tag('a', array('class' => 'phabricator-main-menu-brand', 'href' => '/'), array(javelin_tag('span', array('aural' => true), pht('Home')), $logo_node, $wordmark_node)); }
public static function getLiveIndividualRef() { $cache = PhabricatorCaches::getRequestCache(); $ref = $cache->getKey(self::KEY_INDIVIDUAL); if (!$ref) { $ref = self::newIndividualRef(); $cache->setKey(self::KEY_INDIVIDUAL, $ref); } return $ref; }
protected function applyFinalEffects(PhabricatorLiskDAO $object, array $xactions) { // After making any change to an SSH key, drop the authfile cache so it // is regenerated the next time anyone authenticates. $cache = PhabricatorCaches::getMutableCache(); $authfile_key = PhabricatorAuthSSHKeyQuery::AUTHFILE_CACHEKEY; $cache->deleteKey($authfile_key); return $xactions; }
private function didReadValue($format, $value) { switch ($format) { case self::CACHE_FORMAT_RAW: return $value; case self::CACHE_FORMAT_DEFLATE: return PhabricatorCaches::inflateData($value); default: throw new Exception(pht('Unknown cache format.')); } }
#!/usr/bin/env php <?php $root = dirname(dirname(dirname(__FILE__))); require_once $root . '/scripts/__init_script__.php'; $cache = PhabricatorCaches::getMutableCache(); $authfile_key = PhabricatorAuthSSHKeyQuery::AUTHFILE_CACHEKEY; $authfile = $cache->getKey($authfile_key); if ($authfile === null) { $keys = id(new PhabricatorAuthSSHKeyQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withIsActive(true)->execute(); if (!$keys) { echo pht('No keys found.') . "\n"; exit(1); } $bin = $root . '/bin/ssh-exec'; foreach ($keys as $ssh_key) { $key_argv = array(); $object = $ssh_key->getObject(); if ($object instanceof PhabricatorUser) { $key_argv[] = '--phabricator-ssh-user'; $key_argv[] = $object->getUsername(); } else { if ($object instanceof AlmanacDevice) { if (!$ssh_key->getIsTrusted()) { // If this key is not a trusted device key, don't allow SSH // authentication. continue; } $key_argv[] = '--phabricator-ssh-device'; $key_argv[] = $object->getName(); } else { // We don't know what sort of key this is; don't permit SSH auth.
/** * Retrieve or build a graph cache bucket from the cache. * * Normally, this operates as a readthrough cache call. It can also be used * to force a cache update by passing the existing data to `$rebuild_data`. * * @param int Bucket key, from @{method:getBucketKey}. * @param mixed Current data, to force a cache rebuild of this bucket. * @return array Data from the cache. * @task cache */ private function getBucketData($bucket_key, $rebuild_data = null) { $cache_key = $this->getBucketCacheKey($bucket_key); // TODO: This cache stuff could be handled more gracefully, but the // database cache currently requires values to be strings and needs // some tweaking to support this as part of a stack. Our cache semantics // here are also unusual (not purely readthrough) because this cache is // appendable. $cache_level1 = PhabricatorCaches::getRepositoryGraphL1Cache(); $cache_level2 = PhabricatorCaches::getRepositoryGraphL2Cache(); if ($rebuild_data === null) { $bucket_data = $cache_level1->getKey($cache_key); if ($bucket_data) { return $bucket_data; } $bucket_data = $cache_level2->getKey($cache_key); if ($bucket_data) { $unserialized = @unserialize($bucket_data); if ($unserialized) { // Fill APC if we got a database hit but missed in APC. $cache_level1->setKey($cache_key, $unserialized); return $unserialized; } } } if (!is_array($rebuild_data)) { $rebuild_data = array(); } $bucket_data = $this->rebuildBucket($bucket_key, $rebuild_data); // Don't bother writing the data if we didn't update anything. if ($bucket_data !== $rebuild_data) { $cache_level2->setKey($cache_key, serialize($bucket_data)); $cache_level1->setKey($cache_key, $bucket_data); } return $bucket_data; }
private function writeCacheData(array $keys, array $blame) { $writes = array(); foreach ($keys as $path => $key) { $value = idx($blame, $path); if ($value === null) { continue; } // For now, just store the entire value with a "raw" header. In the // future, we could compress this or use IDs instead. $value = "raw\n" . implode("\n", $value); $writes[$key] = $value; } if (!$writes) { return; } $cache = PhabricatorCaches::getImmutableCache(); $data = $cache->setKeys($writes, phutil_units('14 days in seconds')); }
/** * Destroy the request cache. * * This is called at the beginning of each logical request. * * @return void */ public static function destroyRequestCache() { self::$requestCache = null; }
private function renderPhabricatorLogo() { $style_logo = null; $custom_header = PhabricatorEnv::getEnvConfig('ui.custom-header'); if ($custom_header) { $cache = PhabricatorCaches::getImmutableCache(); $cache_key_logo = 'ui.custom-header.logo-phid.v1.' . $custom_header; $logo_uri = $cache->getKey($cache_key_logo); if (!$logo_uri) { $file = id(new PhabricatorFileQuery())->setViewer($this->getUser())->withPHIDs(array($custom_header))->executeOne(); if ($file) { $logo_uri = $file->getViewURI(); $cache->setKey($cache_key_logo, $logo_uri); } } if ($logo_uri) { $style_logo = 'background-size: 96px 40px; ' . 'background-position: 0px 0px; ' . 'background-image: url(' . $logo_uri . ');'; } } $color = PhabricatorEnv::getEnvConfig('ui.header-color'); if ($color == 'light') { $color = 'dark'; } else { $color = 'light'; } return phutil_tag('a', array('class' => 'phabricator-main-menu-brand', 'href' => '/'), array(javelin_tag('span', array('aural' => true), pht('Home')), phutil_tag('span', array('class' => 'sprite-menu phabricator-main-menu-eye ' . $color . '-eye'), ''), phutil_tag('span', array('class' => 'sprite-menu phabricator-main-menu-logo ' . $color . '-logo', 'style' => $style_logo), ''))); }
/** * @task order */ public function getOrderableColumns() { $cache = PhabricatorCaches::getRequestCache(); $class = get_class($this); $cache_key = 'query.orderablecolumns.' . $class; $columns = $cache->getKey($cache_key); if ($columns !== null) { return $columns; } $columns = array('id' => array('table' => $this->getPrimaryTableAlias(), 'column' => 'id', 'reverse' => false, 'type' => 'int', 'unique' => true)); $object = $this->newResultObject(); if ($object instanceof PhabricatorCustomFieldInterface) { $list = PhabricatorCustomField::getObjectFields($object, PhabricatorCustomField::ROLE_APPLICATIONSEARCH); foreach ($list->getFields() as $field) { $index = $field->buildOrderIndex(); if (!$index) { continue; } $digest = $field->getFieldIndex(); $key = $field->getModernFieldKey(); $columns[$key] = array('table' => 'appsearch_order_' . $digest, 'column' => 'indexValue', 'type' => $index->getIndexValueType(), 'null' => 'tail', 'customfield' => true, 'customfield.index.table' => $index->getTableName(), 'customfield.index.key' => $digest); } } $cache->setKey($cache_key, $columns); return $columns; }
<?php phabricator_startup(); try { PhabricatorStartup::beginStartupPhase('libraries'); PhabricatorStartup::loadCoreLibraries(); PhabricatorStartup::beginStartupPhase('purge'); PhabricatorCaches::destroyRequestCache(); PhabricatorStartup::beginStartupPhase('sink'); $sink = new AphrontPHPHTTPSink(); try { PhabricatorStartup::beginStartupPhase('run'); AphrontApplicationConfiguration::runHTTPRequest($sink); } catch (Exception $ex) { try { $response = new AphrontUnhandledExceptionResponse(); $response->setException($ex); PhabricatorStartup::endOutputCapture(); $sink->writeResponse($response); } catch (Exception $response_exception) { // If we hit a rendering exception, ignore it and throw the original // exception. It is generally more interesting and more likely to be // the root cause. throw $ex; } } } catch (Exception $ex) { PhabricatorStartup::didEncounterFatalException('Core Exception', $ex, false); } function phabricator_startup() {
private function writeHealthRecord(array $record) { $cache = PhabricatorCaches::getSetupCache(); $cache_key = $this->getHealthRecordCacheKey(); $cache->setKey($cache_key, $record); }
/** * @task pull */ protected function run() { $argv = $this->getArgv(); array_unshift($argv, __CLASS__); $args = new PhutilArgumentParser($argv); $args->parse(array(array('name' => 'no-discovery', 'help' => pht('Pull only, without discovering commits.')), array('name' => 'not', 'param' => 'repository', 'repeat' => true, 'help' => pht('Do not pull __repository__.')), array('name' => 'repositories', 'wildcard' => true, 'help' => pht('Pull specific __repositories__ instead of all.')))); $no_discovery = $args->getArg('no-discovery'); $include = $args->getArg('repositories'); $exclude = $args->getArg('not'); // Each repository has an individual pull frequency; after we pull it, // wait that long to pull it again. When we start up, try to pull everything // serially. $retry_after = array(); $min_sleep = 15; $max_futures = 4; $futures = array(); $queue = array(); while (!$this->shouldExit()) { PhabricatorCaches::destroyRequestCache(); $pullable = $this->loadPullableRepositories($include, $exclude); // If any repositories have the NEEDS_UPDATE flag set, pull them // as soon as possible. $need_update_messages = $this->loadRepositoryUpdateMessages(true); foreach ($need_update_messages as $message) { $repo = idx($pullable, $message->getRepositoryID()); if (!$repo) { continue; } $this->log(pht('Got an update message for repository "%s"!', $repo->getMonogram())); $retry_after[$message->getRepositoryID()] = time(); } // If any repositories were deleted, remove them from the retry timer map // so we don't end up with a retry timer that never gets updated and // causes us to sleep for the minimum amount of time. $retry_after = array_select_keys($retry_after, array_keys($pullable)); // Figure out which repositories we need to queue for an update. foreach ($pullable as $id => $repository) { $monogram = $repository->getMonogram(); if (isset($futures[$id])) { $this->log(pht('Repository "%s" is currently updating.', $monogram)); continue; } if (isset($queue[$id])) { $this->log(pht('Repository "%s" is already queued.', $monogram)); continue; } $after = idx($retry_after, $id, 0); if ($after > time()) { $this->log(pht('Repository "%s" is not due for an update for %s second(s).', $monogram, new PhutilNumber($after - time()))); continue; } if (!$after) { $this->log(pht('Scheduling repository "%s" for an initial update.', $monogram)); } else { $this->log(pht('Scheduling repository "%s" for an update (%s seconds overdue).', $monogram, new PhutilNumber(time() - $after))); } $queue[$id] = $after; } // Process repositories in the order they became candidates for updates. asort($queue); // Dequeue repositories until we hit maximum parallelism. while ($queue && count($futures) < $max_futures) { foreach ($queue as $id => $time) { $repository = idx($pullable, $id); if (!$repository) { $this->log(pht('Repository %s is no longer pullable; skipping.', $id)); unset($queue[$id]); continue; } $monogram = $repository->getMonogram(); $this->log(pht('Starting update for repository "%s".', $monogram)); unset($queue[$id]); $futures[$id] = $this->buildUpdateFuture($repository, $no_discovery); break; } } if ($queue) { $this->log(pht('Not enough process slots to schedule the other %s ' . 'repository(s) for updates yet.', new PhutilNumber(count($queue)))); } if ($futures) { $iterator = id(new FutureIterator($futures))->setUpdateInterval($min_sleep); foreach ($iterator as $id => $future) { $this->stillWorking(); if ($future === null) { $this->log(pht('Waiting for updates to complete...')); $this->stillWorking(); if ($this->loadRepositoryUpdateMessages()) { $this->log(pht('Interrupted by pending updates!')); break; } continue; } unset($futures[$id]); $retry_after[$id] = $this->resolveUpdateFuture($pullable[$id], $future, $min_sleep); // We have a free slot now, so go try to fill it. break; } // Jump back into prioritization if we had any futures to deal with. continue; } $this->waitForUpdates($min_sleep, $retry_after); } }
protected function run() { // The trigger daemon is a low-level infrastructure daemon which schedules // and executes chronological events. Examples include a subscription which // generates a bill on the 12th of every month, or a reminder email 15 // minutes before a meeting. // Only one trigger daemon can run at a time, and very little work should // happen in the daemon process. In general, triggered events should // just schedule a task into the normal daemon worker queue and then // return. This allows the real work to take longer to execute without // disrupting other triggers. // The trigger mechanism guarantees that events will execute exactly once, // but does not guarantee that they will execute at precisely the specified // time. Under normal circumstances, they should execute within a minute or // so of the desired time, so this mechanism can be used for things like // meeting reminders. // If the trigger queue backs up (for example, because it is overwhelmed by // trigger updates, doesn't run for a while, or a trigger action is written // inefficiently) or the daemon queue backs up (usually for similar // reasons), events may execute an arbitrarily long time after they were // scheduled to execute. In some cases (like billing a subscription) this // may be desirable; in other cases (like sending a meeting reminder) the // action may want to check the current time and see if the event is still // relevant. // The trigger daemon works in two phases: // // 1. A scheduling phase processes recently updated triggers and // schedules them for future execution. For example, this phase would // see that a meeting trigger had been changed recently, determine // when the reminder for it should execute, and then schedule the // action to execute at that future date. // 2. An execution phase runs the actions for any scheduled events which // are due to execute. // // The major goal of this design is to deliver on the guarantee that events // will execute exactly once. It prevents race conditions in scheduling // and execution by ensuring there is only one writer for either of these // phases. Without this separation of responsibilities, web processes // trying to reschedule events after an update could race with other web // processes or the daemon. // We want to start the first GC cycle right away, not wait 4 hours. $this->nextCollection = PhabricatorTime::getNow(); do { PhabricatorCaches::destroyRequestCache(); $lock = PhabricatorGlobalLock::newLock('trigger'); try { $lock->lock(5); } catch (PhutilLockException $ex) { throw new PhutilProxyException(pht('Another process is holding the trigger lock. Usually, this ' . 'means another copy of the trigger daemon is running elsewhere. ' . 'Multiple processes are not permitted to update triggers ' . 'simultaneously.'), $ex); } // Run the scheduling phase. This finds updated triggers which we have // not scheduled yet and schedules them. $last_version = $this->loadCurrentCursor(); $head_version = $this->loadCurrentVersion(); // The cursor points at the next record to process, so we can only skip // this step if we're ahead of the version number. if ($last_version <= $head_version) { $this->scheduleTriggers($last_version); } // Run the execution phase. This finds events which are due to execute // and runs them. $this->executeTriggers(); $lock->unlock(); $sleep_duration = $this->getSleepDuration(); $sleep_duration = $this->runNuanceImportCursors($sleep_duration); $sleep_duration = $this->runGarbageCollection($sleep_duration); $sleep_duration = $this->runCalendarNotifier($sleep_duration); $this->sleep($sleep_duration); } while (!$this->shouldExit()); }
/** * Test if we've survived through setup on at least one normal request * without fataling. * * If we've made it through setup without hitting any fatals, we switch * to render a more friendly error page when encountering issues like * database connection failures. This gives users a smoother experience in * the face of intermittent failures. * * @return bool True if we've made it through setup since the last restart. */ public static final function isInFlight() { $cache = PhabricatorCaches::getServerStateCache(); return (bool) $cache->getKey('phabricator.in-flight'); }
public function forcePopulatePKCS8Cache($pkcs8_key) { $entire_key = $this->getEntireKey(); $cache_key = $this->getPKCS8CacheKey($entire_key); $cache = PhabricatorCaches::getImmutableCache(); $cache->setKey($cache_key, $pkcs8_key); }
public static function getViewerSpaces(PhabricatorUser $viewer) { $cache = PhabricatorCaches::getRequestCache(); $cache_key = self::KEY_VIEWER . '(' . $viewer->getPHID() . ')'; $result = $cache->getKey($cache_key); if ($result === null) { $spaces = self::getAllSpaces(); $result = array(); foreach ($spaces as $key => $space) { $can_see = PhabricatorPolicyFilter::hasCapability($viewer, $space, PhabricatorPolicyCapability::CAN_VIEW); if ($can_see) { $result[$key] = $space; } } $cache->setKey($cache_key, $result); } return $result; }
protected final function getTransactionHint(PhabricatorPolicyInterface $object) { $cache = PhabricatorCaches::getRequestCache(); return $cache->getKey(self::getObjectPolicyCacheKey($object, $this)); }
public static final function deleteSetupCheckCache() { $cache = PhabricatorCaches::getSetupCache(); $cache->deleteKeys(array('phabricator.setup.needs-repair', 'phabricator.setup.issue-keys')); }
protected function serveResource(array $spec) { $path = $spec['path']; $hash = idx($spec, 'hash'); // Sanity checking to keep this from exposing anything sensitive, since it // ultimately boils down to disk reads. if (preg_match('@(//|\\.\\.)@', $path)) { return new Aphront400Response(); } $type = CelerityResourceTransformer::getResourceType($path); $type_map = self::getSupportedResourceTypes(); if (empty($type_map[$type])) { throw new Exception(pht('Only static resources may be served.')); } $dev_mode = PhabricatorEnv::getEnvConfig('phabricator.developer-mode'); $map = $this->getCelerityResourceMap(); $expect_hash = $map->getHashForName($path); // Test if the URI hash is correct for our current resource map. If it // is not, refuse to cache this resource. This avoids poisoning caches // and CDNs if we're getting a request for a new resource to an old node // shortly after a push. $is_cacheable = $hash === $expect_hash; $is_locally_cacheable = $this->isLocallyCacheableResourceType($type); if (AphrontRequest::getHTTPHeader('If-Modified-Since') && $is_cacheable) { // Return a "304 Not Modified". We don't care about the value of this // field since we never change what resource is served by a given URI. return $this->makeResponseCacheable(new Aphront304Response()); } $cache = null; $data = null; if ($is_cacheable && $is_locally_cacheable && !$dev_mode) { $cache = PhabricatorCaches::getImmutableCache(); $request_path = $this->getRequest()->getPath(); $cache_key = $this->getCacheKey($request_path); $data = $cache->getKey($cache_key); } if ($data === null) { if ($map->isPackageResource($path)) { $resource_names = $map->getResourceNamesForPackageName($path); if (!$resource_names) { return new Aphront404Response(); } try { $data = array(); foreach ($resource_names as $resource_name) { $data[] = $map->getResourceDataForName($resource_name); } $data = implode("\n\n", $data); } catch (Exception $ex) { return new Aphront404Response(); } } else { try { $data = $map->getResourceDataForName($path); } catch (Exception $ex) { return new Aphront404Response(); } } $xformer = $this->buildResourceTransformer(); if ($xformer) { $data = $xformer->transformResource($path, $data); } if ($cache) { $cache->setKey($cache_key, $data); } } $response = new AphrontFileResponse(); $response->setContent($data); $response->setMimeType($type_map[$type]); // NOTE: This is a piece of magic required to make WOFF fonts work in // Firefox and IE. Possibly we should generalize this more. $cross_origin_types = array('woff' => true, 'woff2' => true, 'eot' => true); if (isset($cross_origin_types[$type])) { // We could be more tailored here, but it's not currently trivial to // generate a comprehensive list of valid origins (an install may have // arbitrarily many Phame blogs, for example), and we lose nothing by // allowing access from anywhere. $response->addAllowOrigin('*'); } if ($is_cacheable) { $response = $this->makeResponseCacheable($response); } return $response; }
private static function addNamespaceToCaches(array $caches) { $namespace = PhabricatorCaches::getNamespace(); if (!$namespace) { return $caches; } foreach ($caches as $key => $cache) { $ncache = new PhutilKeyValueCacheNamespace($cache); $ncache->setNamespace($namespace); $caches[$key] = $ncache; } return $caches; }