public function renderSingleResource($symbol) { $map = CelerityResourceMap::getInstance(); $resolved = $map->resolveResources(array($symbol)); $packaged = $map->packageResources($resolved); return $this->renderPackagedResources($packaged); }
/** * Get the versioned URI for a raw resource, like an image. * * @param string Path to the raw image. * @return string Versioned path to the image, if one is available. * * @group celerity */ function celerity_get_resource_uri($resource) { $map = CelerityResourceMap::getInstance(); $info = $map->lookupFileInformation($resource); if ($info) { return $info['uri']; } else { return $resource; } }
public function processRequest() { $path = $this->path; // Sanity checking to keep this from exposing anything sensitive. $path = preg_replace('@(//|\\.\\.)@', '', $path); $matches = null; if (!preg_match('/\\.(css|js)$/', $path, $matches)) { throw new Exception("Only CSS and JS resources may be served."); } if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !PhabricatorEnv::getEnvConfig('celerity.force-disk-reads')) { // 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()); } $type = $matches[1]; $root = dirname(phutil_get_library_root('phabricator')); if ($this->package) { $map = CelerityResourceMap::getInstance(); $paths = $map->resolvePackage($this->hash); if (!$paths) { return new Aphront404Response(); } try { $data = array(); foreach ($paths as $path) { $data[] = Filesystem::readFile($root . '/webroot/' . $path); } $data = implode("\n\n", $data); } catch (Exception $ex) { return new Aphront404Response(); } } else { try { $data = Filesystem::readFile($root . '/webroot/' . $path); } catch (Exception $ex) { return new Aphront404Response(); } } $response = new AphrontFileResponse(); $response->setContent($data); switch ($type) { case 'css': $response->setMimeType("text/css; charset=utf-8"); break; case 'js': $response->setMimeType("text/javascript; charset=utf-8"); break; } return $this->makeResponseCacheable($response); }
public function processRequest() { $path = $this->path; // 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 = $this->getSupportedResourceTypes(); if (empty($type_map[$type])) { throw new Exception("Only static resources may be served."); } if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !PhabricatorEnv::getEnvConfig('celerity.force-disk-reads')) { // 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()); } $root = dirname(phutil_get_library_root('phabricator')); if ($this->package) { $map = CelerityResourceMap::getInstance(); $paths = $map->resolvePackage($this->hash); if (!$paths) { return new Aphront404Response(); } try { $data = array(); foreach ($paths as $package_path) { $data[] = Filesystem::readFile($root . '/webroot/' . $package_path); } $data = implode("\n\n", $data); } catch (Exception $ex) { return new Aphront404Response(); } } else { try { $data = Filesystem::readFile($root . '/webroot/' . $path); } catch (Exception $ex) { return new Aphront404Response(); } } $xformer = new CelerityResourceTransformer(); $xformer->setMinify(PhabricatorEnv::getEnvConfig('celerity.minify')); $xformer->setCelerityMap(CelerityResourceMap::getInstance()); $data = $xformer->transformResource($path, $data); $response = new AphrontFileResponse(); $response->setContent($data); $response->setMimeType($type_map[$type]); return $this->makeResponseCacheable($response); }
/** * NOTE: This is an ADVANCED feature that improves performance but adds a lot * of complexity! This is only suitable for production servers because workers * won't pick up changes between when they spawn and when they handle a request. * * Phabricator spends a significant portion of its runtime loading classes * and functions, even with APC enabled. Since we have very rigidly-defined * rules about what can go in a module (specifically: no side effects), it * is safe to load all the libraries *before* we receive a request. * * Normally, SAPIs don't provide a way to do this, but with a patched PHP-FPM * SAPI you can provide a warmup file that it will execute before a request * is received. * * We're limited in what we can do here, since request information won't * exist yet, but we can load class and function definitions, which is what * we're really interested in. * * Once this file exists, the FCGI process will drop into its normal accept loop * and eventually process a request. */ function __warmup__() { $root = dirname(dirname(dirname(dirname(__FILE__)))); require_once $root . '/libphutil/src/__phutil_library_init__.php'; require_once $root . '/arcanist/src/__phutil_library_init__.php'; require_once $root . '/phabricator/src/__phutil_library_init__.php'; // Load every symbol. We could possibly refine this -- we don't need to load // every Controller, for instance. $loader = new PhutilSymbolLoader(); $loader->selectAndLoadSymbols(); // Force load of the Celerity map. CelerityResourceMap::getInstance(); define('__WARMUP__', true); }
public function lintPath($path) { if (!$this->haveSymbolsBinary) { if (!$this->haveWarnedAboutBinary) { $this->haveWarnedAboutBinary = true; // TODO: Write build documentation for the Javelin binaries and point // the user at it. $this->raiseLintAtLine(1, 0, self::LINT_MISSING_BINARY, "The 'javelinsymbols' binary in the Javelin project has not been " . "built, so the Javelin linter can't run. This isn't a big concern, " . "but means some Javelin problems can't be automatically detected."); } return; } list($uses, $installs) = $this->getUsedAndInstalledSymbolsForPath($path); foreach ($uses as $symbol => $line) { $parts = explode('.', $symbol); foreach ($parts as $part) { if ($part[0] == '_' && $part[1] != '_') { $base = implode('.', array_slice($parts, 0, 2)); if (!array_key_exists($base, $installs)) { $this->raiseLintAtLine($line, 0, self::LINT_PRIVATE_ACCESS, "This file accesses private symbol '{$symbol}' across file " . "boundaries. You may only access private members and methods " . "from the file where they are defined."); } break; } } } if ($this->getEngine()->getCommitHookMode()) { // Don't do the dependency checks in commit-hook mode because we won't // have an available working copy. return; } $external_classes = array(); foreach ($uses as $symbol => $line) { $parts = explode('.', $symbol); $class = implode('.', array_slice($parts, 0, 2)); if (!array_key_exists($class, $external_classes) && !array_key_exists($class, $installs)) { $external_classes[$class] = $line; } } $celerity = CelerityResourceMap::getInstance(); $path = preg_replace('@^externals/javelin/src/@', 'webroot/rsrc/js/javelin/', $path); $need = $external_classes; $info = $celerity->lookupFileInformation(substr($path, strlen('webroot'))); if (!$info) { $info = array(); } $requires = idx($info, 'requires', array()); foreach ($requires as $key => $name) { $symbol_info = $celerity->lookupSymbolInformation($name); if (!$symbol_info) { $this->raiseLintAtLine(0, 0, self::LINT_UNKNOWN_DEPENDENCY, "This file @requires component '{$name}', but it does not " . "exist. You may need to rebuild the Celerity map."); unset($requires[$key]); continue; } $symbol_path = 'webroot' . $symbol_info['disk']; list($ignored, $req_install) = $this->getUsedAndInstalledSymbolsForPath($symbol_path); if (array_intersect_key($req_install, $external_classes)) { $need = array_diff_key($need, $req_install); unset($requires[$key]); } } foreach ($need as $class => $line) { $this->raiseLintAtLine($line, 0, self::LINT_MISSING_DEPENDENCY, "This file uses '{$class}' but does not @requires the component " . "which installs it. You may need to rebuild the Celerity map."); } foreach ($requires as $component) { $this->raiseLintAtLine(0, 0, self::LINT_UNNECESSARY_DEPENDENCY, "This file @requires component '{$component}' but does not use " . "anything it provides."); } }
/** * Registers a resource map for Celerity. This is glue code between the Celerity * mapper script and @{class:CelerityResourceMap}. * * @group celerity */ function celerity_register_resource_map(array $map, array $package_map) { $instance = CelerityResourceMap::getInstance(); $instance->setResourceMap($map); $instance->setPackageMap($package_map); }