public static function getInstance()
 {
     if (!self::$instance) {
         self::$instance = new PhutilBootloader();
     }
     return self::$instance;
 }
 private function loadVersions(PhabricatorUser $viewer)
 {
     $specs = array('phabricator', 'arcanist', 'phutil');
     $all_libraries = PhutilBootloader::getInstance()->getAllLibraries();
     // This puts the core libraries at the top:
     $other_libraries = array_diff($all_libraries, $specs);
     $specs = array_merge($specs, $other_libraries);
     $futures = array();
     foreach ($specs as $lib) {
         $root = dirname(phutil_get_library_root($lib));
         $futures[$lib] = id(new ExecFuture('git log --format=%s -n 1 --', '%H %ct'))->setCWD($root);
     }
     $results = array();
     foreach ($futures as $key => $future) {
         list($err, $stdout) = $future->resolve();
         if (!$err) {
             list($hash, $epoch) = explode(' ', $stdout);
             $version = pht('%s (%s)', $hash, phabricator_date($epoch, $viewer));
         } else {
             $version = pht('Unknown');
         }
         $results[$key] = $version;
     }
     return $results;
 }
 public function generateData()
 {
     $lib_data = array();
     foreach (PhutilBootloader::getInstance()->getAllLibraries() as $lib) {
         $lib_data[$lib] = phutil_get_library_root($lib);
     }
     return array('config' => PhabricatorEnv::getAllConfigKeys(), 'libraries' => $lib_data);
 }
 public function render()
 {
     $user = $this->getUser();
     $trace = $this->trace;
     $libraries = PhutilBootloader::getInstance()->getAllLibraries();
     // TODO: Make this configurable?
     $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/';
     $callsigns = array('arcanist' => 'ARC', 'phutil' => 'PHU', 'phabricator' => 'P');
     $rows = array();
     $depth = count($trace);
     foreach ($trace as $part) {
         $lib = null;
         $file = idx($part, 'file');
         $relative = $file;
         foreach ($libraries as $library) {
             $root = phutil_get_library_root($library);
             if (Filesystem::isDescendant($file, $root)) {
                 $lib = $library;
                 $relative = Filesystem::readablePath($file, $root);
                 break;
             }
         }
         $where = '';
         if (isset($part['class'])) {
             $where .= $part['class'] . '::';
         }
         if (isset($part['function'])) {
             $where .= $part['function'] . '()';
         }
         if ($file) {
             if (isset($callsigns[$lib])) {
                 $attrs = array('title' => $file);
                 try {
                     $attrs['href'] = $user->loadEditorLink('/src/' . $relative, $part['line'], $callsigns[$lib]);
                 } catch (Exception $ex) {
                     // The database can be inaccessible.
                 }
                 if (empty($attrs['href'])) {
                     $attrs['href'] = sprintf($path, $callsigns[$lib]) . str_replace(DIRECTORY_SEPARATOR, '/', $relative) . '$' . $part['line'];
                     $attrs['target'] = '_blank';
                 }
                 $file_name = phutil_tag('a', $attrs, $relative);
             } else {
                 $file_name = phutil_tag('span', array('title' => $file), $relative);
             }
             $file_name = hsprintf('%s : %d', $file_name, $part['line']);
         } else {
             $file_name = phutil_tag('em', array(), '(Internal)');
         }
         $rows[] = array($depth--, $lib, $file_name, $where);
     }
     $table = new AphrontTableView($rows);
     $table->setHeaders(array(pht('Depth'), pht('Library'), pht('File'), pht('Where')));
     $table->setColumnClasses(array('n', '', '', 'wide'));
     return phutil_tag('div', array('class' => 'exception-trace'), $table->render());
 }
 /**
  * This is more of an acceptance test case instead of a unit test. It verifies
  * that all the library map is up-to-date.
  */
 public function testLibraryMap()
 {
     $library = phutil_get_current_library_name();
     $root = phutil_get_library_root($library);
     $new_library_map = id(new PhutilLibraryMapBuilder($root))->buildMap();
     $bootloader = PhutilBootloader::getInstance();
     $old_library_map = $bootloader->getLibraryMapWithoutExtensions($library);
     unset($old_library_map[PhutilLibraryMapBuilder::LIBRARY_MAP_VERSION_KEY]);
     $this->assertEqual($new_library_map, $old_library_map, 'The library map does not appear to be up-to-date. Try ' . 'rebuilding the map with `arc liberate`.');
 }
Exemplo n.º 6
0
function phutil_get_library_name_for_root($path)
{
    $path = rtrim(Filesystem::resolvePath($path), '/');
    $bootloader = PhutilBootloader::getInstance();
    $libraries = $bootloader->getAllLibraries();
    foreach ($libraries as $library) {
        $root = $bootloader->getLibraryRoot($library);
        if (rtrim(Filesystem::resolvePath($root), '/') == $path) {
            return $library;
        }
    }
    return null;
}
Exemplo n.º 7
0
 /**
  * This is more of an acceptance test case instead of a unit test. It verifies
  * that all the library map is up-to-date.
  */
 public function testLibraryMap()
 {
     $root = $this->getLibraryRoot();
     $library = phutil_get_library_name_for_root($root);
     $new_library_map = id(new PhutilLibraryMapBuilder($root))->buildMap();
     $bootloader = PhutilBootloader::getInstance();
     $old_library_map = $bootloader->getLibraryMapWithoutExtensions($library);
     unset($old_library_map[PhutilLibraryMapBuilder::LIBRARY_MAP_VERSION_KEY]);
     $identical = $new_library_map === $old_library_map;
     if (!$identical) {
         $differences = $this->getMapDifferences($old_library_map, $new_library_map);
         sort($differences);
     } else {
         $differences = array();
     }
     $this->assertTrue($identical, pht("The library map is out of date. Rebuild it with `%s`.\n" . "These entries differ: %s.", 'arc liberate', implode(', ', $differences)));
 }
 private function renderStackTrace($trace)
 {
     $libraries = PhutilBootloader::getInstance()->getAllLibraries();
     // TODO: Make this configurable?
     $host = 'https://secure.phabricator.com';
     $browse = array('arcanist' => $host . '/diffusion/ARC/browse/origin:master/src/', 'phutil' => $host . '/diffusion/PHU/browse/origin:master/src/', 'phabricator' => $host . '/diffusion/P/browse/origin:master/src/');
     $rows = array();
     $depth = count($trace);
     foreach ($trace as $part) {
         $lib = null;
         $file = idx($part, 'file');
         $relative = $file;
         foreach ($libraries as $library) {
             $root = phutil_get_library_root($library);
             if (Filesystem::isDescendant($file, $root)) {
                 $lib = $library;
                 $relative = Filesystem::readablePath($file, $root);
                 break;
             }
         }
         $where = '';
         if (isset($part['class'])) {
             $where .= $part['class'] . '::';
         }
         if (isset($part['function'])) {
             $where .= $part['function'] . '()';
         }
         if ($file) {
             if (isset($browse[$lib])) {
                 $file_name = phutil_render_tag('a', array('href' => $browse[$lib] . $relative . '$' . $part['line'], 'title' => $file, 'target' => '_blank'), phutil_escape_html($relative));
             } else {
                 $file_name = phutil_render_tag('span', array('title' => $file), phutil_escape_html($relative));
             }
             $file_name = $file_name . ' : ' . (int) $part['line'];
         } else {
             $file_name = '<em>(Internal)</em>';
         }
         $rows[] = array($depth--, phutil_escape_html($lib), $file_name, phutil_escape_html($where));
     }
     $table = new AphrontTableView($rows);
     $table->setHeaders(array('Depth', 'Library', 'File', 'Where'));
     $table->setColumnClasses(array('n', '', '', 'wide'));
     return '<div class="exception-trace">' . '<div class="exception-trace-header">Stack Trace</div>' . $table->render() . '</div>';
 }
Exemplo n.º 9
0
 public function run()
 {
     $bootloader = PhutilBootloader::getInstance();
     $affected_modules = array();
     foreach ($this->getPaths() as $path) {
         $library_root = phutil_get_library_root_for_path($path);
         if (!$library_root) {
             continue;
         }
         $library_name = phutil_get_library_name_for_root($library_root);
         if (!$library_name) {
             throw new Exception("Attempting to run unit tests on a libphutil library which has not " . "been loaded, at:\n\n" . "    {$library_root}\n\n" . "This probably means one of two things:\n\n" . "    - You may need to add this library to .arcconfig.\n" . "    - You may be running tests on a copy of libphutil or arcanist\n" . "      using a different copy of libphutil or arcanist. This\n" . "      operation is not supported.");
         }
         $path = Filesystem::resolvePath($path);
         if (!is_dir($path)) {
             $path = dirname($path);
         }
         if ($path == $library_root) {
             continue;
         }
         $library_path = Filesystem::readablePath($path, $library_root);
         do {
             // Add the module and all parent modules as affected modules, which
             // means we'll look for __tests__ to run here and in any containing
             // module.
             $affected_modules[$library_name . ':' . $library_path] = array('name' => $library_name, 'root' => $library_root, 'path' => $library_path);
             $library_path = dirname($library_path);
         } while ($library_path != '.');
     }
     $tests = array();
     foreach ($affected_modules as $library_info) {
         $library_name = $library_info['name'];
         $library_root = $library_info['root'];
         $module = $library_info['path'];
         if (basename($module) == '__tests__') {
             // Okay, this is a __tests__ module.
         } else {
             $exists = $bootloader->moduleExists($library_name, $module . '/__tests__');
             if ($exists) {
                 // This is a module which has a __tests__ module in it.
                 $module .= '/__tests__';
             } else {
                 // Look for a parent named __tests__.
                 $rpos = strrpos($module, '/__tests__');
                 if ($rpos === false) {
                     // No tests to run since there is no child or parent module named
                     // __tests__.
                     continue;
                 }
                 // Select the parent named __tests__.
                 $module = substr($module, 0, $rpos + strlen('/__tests__'));
             }
         }
         $module_key = $library_name . ':' . $module;
         $tests[$module_key] = array('library' => $library_name, 'root' => $library_root, 'module' => $module);
     }
     if (!$tests) {
         throw new ArcanistNoEffectException("No tests to run.");
     }
     $run_tests = array();
     foreach ($tests as $test) {
         $symbols = id(new PhutilSymbolLoader())->setType('class')->setLibrary($test['library'])->setModule($test['module'])->setAncestorClass('ArcanistPhutilTestCase')->selectAndLoadSymbols();
         foreach ($symbols as $symbol) {
             $run_tests[$symbol['name']] = true;
         }
     }
     $run_tests = array_keys($run_tests);
     if (!$run_tests) {
         throw new ArcanistNoEffectException("No tests to run. You may need to rebuild the phutil library map.");
     }
     $enable_coverage = $this->getEnableCoverage();
     if ($enable_coverage !== false) {
         if (!function_exists('xdebug_start_code_coverage')) {
             if ($enable_coverage === true) {
                 throw new ArcanistUsageException("You specified --coverage but xdebug is not available, so " . "coverage can not be enabled for PhutilUnitTestEngine.");
             }
         } else {
             $enable_coverage = true;
         }
     }
     $results = array();
     foreach ($run_tests as $test_class) {
         PhutilSymbolLoader::loadClass($test_class);
         $test_case = newv($test_class, array());
         $test_case->setEnableCoverage($enable_coverage);
         $test_case->setProjectRoot($this->getWorkingCopy()->getProjectRoot());
         $test_case->setPaths($this->getPaths());
         $results[] = $test_case->run();
     }
     if ($results) {
         $results = call_user_func_array('array_merge', $results);
     }
     return $results;
 }
 /**
  * @task internal
  */
 private function loadSymbol(array $symbol_spec)
 {
     // Check if we've already loaded the symbol; bail if we have.
     $name = $symbol_spec['name'];
     $is_function = $symbol_spec['type'] == 'function';
     if ($is_function) {
         if (function_exists($name)) {
             return;
         }
     } else {
         if (class_exists($name, false) || interface_exists($name, false)) {
             return;
         }
     }
     $lib_name = $symbol_spec['library'];
     $where = $symbol_spec['where'];
     $bootloader = PhutilBootloader::getInstance();
     $bootloader->loadLibrarySource($lib_name, $where);
     // Check that we successfully loaded the symbol from wherever it was
     // supposed to be defined.
     $load_failed = null;
     if ($is_function) {
         if (!function_exists($name)) {
             $load_failed = 'function';
         }
     } else {
         if (!class_exists($name, false) && !interface_exists($name, false)) {
             $load_failed = 'class or interface';
         }
     }
     if ($load_failed !== null) {
         $lib_path = phutil_get_library_root($lib_name);
         throw new PhutilMissingSymbolException($name, $load_failed, "the symbol map for library '{$lib_name}' (at '{$lib_path}') claims " . "this {$load_failed} is defined in '{$where}', but loading that " . "source file did not cause the {$load_failed} to become defined.");
     }
 }
 public function launchDaemon($daemon, array $argv, $debug = false)
 {
     $symbols = $this->loadAvailableDaemonClasses();
     $symbols = ipull($symbols, 'name', 'name');
     if (empty($symbols[$daemon])) {
         throw new Exception("Daemon '{$daemon}' is not known.");
     }
     $pid_dir = $this->getControlDirectory('pid');
     $log_dir = $this->getControlDirectory('log') . '/daemons.log';
     $libphutil_root = dirname(phutil_get_library_root('phutil'));
     $launch_daemon = $libphutil_root . '/scripts/daemon/';
     // TODO: This should be a much better user experience.
     Filesystem::assertExists($pid_dir);
     Filesystem::assertIsDirectory($pid_dir);
     Filesystem::assertWritable($pid_dir);
     foreach ($argv as $key => $arg) {
         $argv[$key] = escapeshellarg($arg);
     }
     $bootloader = PhutilBootloader::getInstance();
     $all_libraries = $bootloader->getAllLibraries();
     $non_default_libraries = array_diff($all_libraries, array('phutil', 'phabricator'));
     $extra_libraries = array();
     foreach ($non_default_libraries as $library) {
         $extra_libraries[] = csprintf('--load-phutil-library=%s', phutil_get_library_root($library));
     }
     $command = csprintf("./launch_daemon.php " . "%s " . "--load-phutil-library=%s " . "%C " . "--conduit-uri=%s " . "--phd=%s " . ($debug ? '--trace ' : '--daemonize '), $daemon, phutil_get_library_root('phabricator'), implode(' ', $extra_libraries), PhabricatorEnv::getURI('/api/'), $pid_dir);
     if (!$debug) {
         // If we're running "phd debug", send output straight to the console
         // instead of to a logfile.
         $command = csprintf("%C --log=%s", $command, $log_dir);
     }
     // Append the daemon's argv.
     $command = csprintf("%C %C", $command, implode(' ', $argv));
     if ($debug) {
         // Don't terminate when the user sends ^C; it will be sent to the
         // subprocess which will terminate normally.
         pcntl_signal(SIGINT, array('PhabricatorDaemonControl', 'ignoreSignal'));
         echo "\n    libphutil/scripts/daemon/ \$ {$command}\n\n";
         phutil_passthru('(cd %s && exec %C)', $launch_daemon, $command);
     } else {
         $future = new ExecFuture('exec %C', $command);
         // Play games to keep 'ps' looking reasonable.
         $future->setCWD($launch_daemon);
         $future->resolvex();
     }
 }
Exemplo n.º 12
0
 public function run()
 {
     $bootloader = PhutilBootloader::getInstance();
     $project_root = $this->getWorkingCopy()->getProjectRoot();
     $look_here = array();
     foreach ($this->getPaths() as $path) {
         $library_root = phutil_get_library_root_for_path($path);
         if (!$library_root) {
             continue;
         }
         $library_name = phutil_get_library_name_for_root($library_root);
         if (!$library_name) {
             throw new Exception("Attempting to run unit tests on a libphutil library which has not " . "been loaded, at:\n\n" . "    {$library_root}\n\n" . "This probably means one of two things:\n\n" . "    - You may need to add this library to .arcconfig.\n" . "    - You may be running tests on a copy of libphutil or arcanist\n" . "      using a different copy of libphutil or arcanist. This\n" . "      operation is not supported.");
         }
         $path = Filesystem::resolvePath($path, $project_root);
         if (!is_dir($path)) {
             $path = dirname($path);
         }
         if ($path == $library_root) {
             continue;
         }
         if (!Filesystem::isDescendant($path, $library_root)) {
             // We have encountered some kind of symlink maze -- for instance, $path
             // is some symlink living outside the library that links into some file
             // inside the library. Just ignore these cases, since the affected file
             // does not actually lie within the library.
             continue;
         }
         $library_path = Filesystem::readablePath($path, $library_root);
         do {
             $look_here[$library_name . ':' . $library_path] = array('library' => $library_name, 'path' => $library_path);
             $library_path = dirname($library_path);
         } while ($library_path != '.');
     }
     // Look for any class that extends ArcanistPhutilTestCase inside a
     // __tests__ directory in any parent directory of every affected file.
     //
     // The idea is that "infrastructure/__tests__/" tests defines general tests
     // for all of "infrastructure/", and those tests run for any change in
     // "infrastructure/". However, "infrastructure/concrete/rebar/__tests__/"
     // defines more specific tests that run only when rebar/ (or some
     // subdirectory) changes.
     $run_tests = array();
     foreach ($look_here as $path_info) {
         $library = $path_info['library'];
         $path = $path_info['path'];
         $symbols = id(new PhutilSymbolLoader())->setType('class')->setLibrary($library)->setPathPrefix($path . '/__tests__/')->setAncestorClass('ArcanistPhutilTestCase')->setConcreteOnly(true)->selectAndLoadSymbols();
         foreach ($symbols as $symbol) {
             $run_tests[$symbol['name']] = true;
         }
     }
     $run_tests = array_keys($run_tests);
     if (!$run_tests) {
         throw new ArcanistNoEffectException("No tests to run.");
     }
     $enable_coverage = $this->getEnableCoverage();
     if ($enable_coverage !== false) {
         if (!function_exists('xdebug_start_code_coverage')) {
             if ($enable_coverage === true) {
                 throw new ArcanistUsageException("You specified --coverage but xdebug is not available, so " . "coverage can not be enabled for PhutilUnitTestEngine.");
             }
         } else {
             $enable_coverage = true;
         }
     }
     $results = array();
     foreach ($run_tests as $test_class) {
         $test_case = newv($test_class, array());
         $test_case->setEnableCoverage($enable_coverage);
         $test_case->setProjectRoot($project_root);
         $test_case->setPaths($this->getPaths());
         $results[] = $test_case->run();
     }
     if ($results) {
         $results = call_user_func_array('array_merge', $results);
     }
     return $results;
 }
 public function willLintPaths(array $paths)
 {
     if (!xhpast_is_available()) {
         throw new Exception(xhpast_get_build_instructions());
     }
     // NOTE: For now, we completely ignore paths and just lint every library in
     // its entirety. This is simpler and relatively fast because we don't do any
     // detailed checks and all the data we need for this comes out of module
     // caches.
     $bootloader = PhutilBootloader::getInstance();
     $libs = $bootloader->getAllLibraries();
     // Load the up-to-date map for each library, without loading the library
     // itself. This means lint results will accurately reflect the state of
     // the working copy.
     $symbols = array();
     foreach ($libs as $lib) {
         $root = phutil_get_library_root($lib);
         try {
             $symbols[$lib] = id(new PhutilLibraryMapBuilder($root))->buildFileSymbolMap();
         } catch (XHPASTSyntaxErrorException $ex) {
             // If the library contains a syntax error then there isn't much that we
             // can do.
             continue;
         }
     }
     $all_symbols = array();
     foreach ($symbols as $library => $map) {
         // Check for files which declare more than one class/interface in the same
         // file, or mix function definitions with class/interface definitions. We
         // must isolate autoloadable symbols to one per file so the autoloader
         // can't end up in an unresolvable cycle.
         foreach ($map as $file => $spec) {
             $have = idx($spec, 'have', array());
             $have_classes = idx($have, 'class', array()) + idx($have, 'interface', array());
             $have_functions = idx($have, 'function');
             if ($have_functions && $have_classes) {
                 $function_list = implode(', ', array_keys($have_functions));
                 $class_list = implode(', ', array_keys($have_classes));
                 $this->raiseLintInLibrary($library, $file, end($have_functions), self::LINT_ONE_CLASS_PER_FILE, "File '{$file}' mixes function ({$function_list}) and " . "class/interface ({$class_list}) definitions in the same file. " . "A file which declares a class or an interface MUST " . "declare nothing else.");
             } else {
                 if (count($have_classes) > 1) {
                     $class_list = implode(', ', array_keys($have_classes));
                     $this->raiseLintInLibrary($library, $file, end($have_classes), self::LINT_ONE_CLASS_PER_FILE, "File '{$file}' declares more than one class or interface " . "({$class_list}). A file which declares a class or interface MUST " . "declare nothing else.");
                 }
             }
         }
         // Check for duplicate symbols: two files providing the same class or
         // function.
         foreach ($map as $file => $spec) {
             $have = idx($spec, 'have', array());
             foreach (array('class', 'function', 'interface') as $type) {
                 $libtype = $type == 'interface' ? 'class' : $type;
                 foreach (idx($have, $type, array()) as $symbol => $offset) {
                     if (empty($all_symbols[$libtype][$symbol])) {
                         $all_symbols[$libtype][$symbol] = array('library' => $library, 'file' => $file, 'offset' => $offset);
                         continue;
                     }
                     $osrc = $all_symbols[$libtype][$symbol]['file'];
                     $olib = $all_symbols[$libtype][$symbol]['library'];
                     $this->raiseLintInLibrary($library, $file, $offset, self::LINT_DUPLICATE_SYMBOL, "Definition of {$type} '{$symbol}' in '{$file}' in library " . "'{$library}' duplicates prior definition in '{$osrc}' in " . "library '{$olib}'.");
                 }
             }
         }
     }
     $types = array('class', 'function', 'interface', 'class/interface');
     foreach ($symbols as $library => $map) {
         // Check for unknown symbols: uses of classes, functions or interfaces
         // which are not defined anywhere. We reference the list of all symbols
         // we built up earlier.
         foreach ($map as $file => $spec) {
             $need = idx($spec, 'need', array());
             foreach ($types as $type) {
                 $libtype = $type;
                 if ($type == 'interface' || $type == 'class/interface') {
                     $libtype = 'class';
                 }
                 foreach (idx($need, $type, array()) as $symbol => $offset) {
                     if (!empty($all_symbols[$libtype][$symbol])) {
                         // Symbol is defined somewhere.
                         continue;
                     }
                     $libphutil_root = dirname(phutil_get_library_root('phutil'));
                     $this->raiseLintInLibrary($library, $file, $offset, self::LINT_UNKNOWN_SYMBOL, "Use of unknown {$type} '{$symbol}'. Common causes are:\n\n" . "  - Your libphutil/ is out of date.\n" . "    This is the most common cause.\n" . "    Update this copy of libphutil: {$libphutil_root}\n" . "\n" . "  - Some other library is out of date.\n" . "    Update the library this symbol appears in.\n" . "\n" . "  - This symbol is misspelled.\n" . "    Spell the symbol name correctly.\n" . "    Symbol name spelling is case-sensitive.\n" . "\n" . "  - This symbol was added recently.\n" . "    Run `arc liberate` on the library it was added to.\n" . "\n" . "  - This symbol is external. Use `@phutil-external-symbol`.\n" . "    Use `grep` to find usage examples of this directive.\n" . "\n" . "*** ALTHOUGH USUALLY EASY TO FIX, THIS IS A SERIOUS ERROR.\n" . "*** THIS ERROR IS YOUR FAULT. YOU MUST RESOLVE IT.");
                 }
             }
         }
     }
 }
Exemplo n.º 14
0
 public static function adjustFilePath($path)
 {
     // Compute known library locations so we can emit relative paths if the
     // file resides inside a known library. This is a little cleaner to read,
     // and limits the number of false positives we get about full path
     // disclosure via HackerOne.
     $bootloader = PhutilBootloader::getInstance();
     $libraries = $bootloader->getAllLibraries();
     $roots = array();
     foreach ($libraries as $library) {
         $root = $bootloader->getLibraryRoot($library);
         // For these libraries, the effective root is one level up.
         switch ($library) {
             case 'phutil':
             case 'arcanist':
             case 'phabricator':
                 $root = dirname($root);
                 break;
         }
         if (!strncmp($root, $path, strlen($root))) {
             return '<' . $library . '>' . substr($path, strlen($root));
         }
     }
     return $path;
 }
Exemplo n.º 15
0
 /**
  * @task internal
  */
 private function loadSymbol(array $symbol_spec)
 {
     // Check if we've already loaded the symbol; bail if we have.
     $name = $symbol_spec['name'];
     $is_function = $symbol_spec['type'] == 'function';
     if ($is_function) {
         if (function_exists($name)) {
             return;
         }
     } else {
         if (class_exists($name, false) || interface_exists($name, false)) {
             return;
         }
     }
     $lib_name = $symbol_spec['library'];
     $bootloader = PhutilBootloader::getInstance();
     $version = $bootloader->getLibraryFormatVersion($lib_name);
     switch ($version) {
         case 1:
             // TODO: Remove this once we drop libphutil v1 support.
             $bootloader->loadModule($symbol_spec['library'], $symbol_spec['module']);
             break;
         case 2:
             $bootloader->loadLibrarySource($symbol_spec['library'], $symbol_spec['where']);
             break;
     }
     // Check that we successfully loaded the symbol from wherever it was
     // supposed to be defined.
     if ($is_function) {
         if (!function_exists($name)) {
             throw new PhutilMissingSymbolException($name);
         }
     } else {
         if (!class_exists($name, false) && !interface_exists($name, false)) {
             throw new PhutilMissingSymbolException($name);
         }
     }
 }
Exemplo n.º 16
0
function phutil_load_library($path)
{
    PhutilBootloader::getInstance()->loadLibrary($path);
}
Exemplo n.º 17
0
 public function launchDaemon($daemon, array $argv, $debug = false)
 {
     $symbols = $this->loadAvailableDaemonClasses();
     $symbols = ipull($symbols, 'name', 'name');
     if (empty($symbols[$daemon])) {
         throw new Exception("Daemon '{$daemon}' is not loaded, misspelled or abstract.");
     }
     $libphutil_root = dirname(phutil_get_library_root('phutil'));
     $launch_daemon = $libphutil_root . '/scripts/daemon/';
     foreach ($argv as $key => $arg) {
         $argv[$key] = escapeshellarg($arg);
     }
     $flags = array();
     if ($debug || PhabricatorEnv::getEnvConfig('phd.trace')) {
         $flags[] = '--trace';
     }
     if ($debug || PhabricatorEnv::getEnvConfig('phd.verbose')) {
         $flags[] = '--verbose';
     }
     if (!$debug) {
         $flags[] = '--daemonize';
     }
     $bootloader = PhutilBootloader::getInstance();
     foreach ($bootloader->getAllLibraries() as $library) {
         if ($library == 'phutil') {
             // No need to load libphutil, it's necessarily loaded implicitly by the
             // daemon itself.
             continue;
         }
         $flags[] = csprintf('--load-phutil-library=%s', phutil_get_library_root($library));
     }
     $flags[] = csprintf('--conduit-uri=%s', PhabricatorEnv::getURI('/api/'));
     if (!$debug) {
         $log_file = $this->getLogDirectory() . '/daemons.log';
         $flags[] = csprintf('--log=%s', $log_file);
     }
     $pid_dir = $this->getPIDDirectory();
     // TODO: This should be a much better user experience.
     Filesystem::assertExists($pid_dir);
     Filesystem::assertIsDirectory($pid_dir);
     Filesystem::assertWritable($pid_dir);
     $flags[] = csprintf('--phd=%s', $pid_dir);
     $command = csprintf('./launch_daemon.php %s %C %C', $daemon, implode(' ', $flags), implode(' ', $argv));
     if ($debug) {
         // Don't terminate when the user sends ^C; it will be sent to the
         // subprocess which will terminate normally.
         pcntl_signal(SIGINT, array('PhabricatorDaemonControl', 'ignoreSignal'));
         echo "\n    libphutil/scripts/daemon/ \$ {$command}\n\n";
         phutil_passthru('(cd %s && exec %C)', $launch_daemon, $command);
     } else {
         $future = new ExecFuture('exec %C', $command);
         // Play games to keep 'ps' looking reasonable.
         $future->setCWD($launch_daemon);
         $future->resolvex();
     }
 }
 private function renderStackTrace($trace, PhabricatorUser $user)
 {
     $libraries = PhutilBootloader::getInstance()->getAllLibraries();
     $version = PhabricatorEnv::getEnvConfig('phabricator.version');
     if (preg_match('/[^a-f0-9]/i', $version)) {
         $version = '';
     }
     // TODO: Make this configurable?
     $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/';
     $callsigns = array('arcanist' => 'ARC', 'phutil' => 'PHU', 'phabricator' => 'P');
     $rows = array();
     $depth = count($trace);
     foreach ($trace as $part) {
         $lib = null;
         $file = idx($part, 'file');
         $relative = $file;
         foreach ($libraries as $library) {
             $root = phutil_get_library_root($library);
             if (Filesystem::isDescendant($file, $root)) {
                 $lib = $library;
                 $relative = Filesystem::readablePath($file, $root);
                 break;
             }
         }
         $where = '';
         if (isset($part['class'])) {
             $where .= $part['class'] . '::';
         }
         if (isset($part['function'])) {
             $where .= $part['function'] . '()';
         }
         if ($file) {
             if (isset($callsigns[$lib])) {
                 $attrs = array('title' => $file);
                 try {
                     $attrs['href'] = $user->loadEditorLink('/src/' . $relative, $part['line'], $callsigns[$lib]);
                 } catch (Exception $ex) {
                     // The database can be inaccessible.
                 }
                 if (empty($attrs['href'])) {
                     $attrs['href'] = sprintf($path, $callsigns[$lib]) . str_replace(DIRECTORY_SEPARATOR, '/', $relative) . ($version && $lib == 'phabricator' ? ';' . $version : '') . '$' . $part['line'];
                     $attrs['target'] = '_blank';
                 }
                 $file_name = phutil_render_tag('a', $attrs, phutil_escape_html($relative));
             } else {
                 $file_name = phutil_render_tag('span', array('title' => $file), phutil_escape_html($relative));
             }
             $file_name = $file_name . ' : ' . (int) $part['line'];
         } else {
             $file_name = '<em>(Internal)</em>';
         }
         $rows[] = array($depth--, phutil_escape_html($lib), $file_name, phutil_escape_html($where));
     }
     $table = new AphrontTableView($rows);
     $table->setHeaders(array('Depth', 'Library', 'File', 'Where'));
     $table->setColumnClasses(array('n', '', '', 'wide'));
     return '<div class="exception-trace">' . '<div class="exception-trace-header">Stack Trace</div>' . $table->render() . '</div>';
 }
Exemplo n.º 19
0
 public function willLintPaths(array $paths)
 {
     if (!xhpast_is_available()) {
         throw new Exception(xhpast_get_build_instructions());
     }
     // NOTE: For now, we completely ignore paths and just lint every library in
     // its entirety. This is simpler and relatively fast because we don't do any
     // detailed checks and all the data we need for this comes out of module
     // caches.
     $bootloader = PhutilBootloader::getInstance();
     $libs = $bootloader->getAllLibraries();
     // Load the up-to-date map for each library, without loading the library
     // itself. This means lint results will accurately reflect the state of
     // the working copy.
     $arc_root = dirname(phutil_get_library_root('arcanist'));
     $bin = "{$arc_root}/scripts/phutil_rebuild_map.php";
     $symbols = array();
     foreach ($libs as $lib) {
         // Do these one at a time since they individually fanout to saturate
         // available system resources.
         $future = new ExecFuture('%s --show --quiet --ugly -- %s', $bin, phutil_get_library_root($lib));
         $symbols[$lib] = $future->resolveJSON();
     }
     $all_symbols = array();
     foreach ($symbols as $library => $map) {
         // Check for files which declare more than one class/interface in the same
         // file, or mix function definitions with class/interface definitions. We
         // must isolate autoloadable symbols to one per file so the autoloader
         // can't end up in an unresolvable cycle.
         foreach ($map as $file => $spec) {
             $have = idx($spec, 'have', array());
             $have_classes = idx($have, 'class', array()) + idx($have, 'interface', array());
             $have_functions = idx($have, 'function');
             if ($have_functions && $have_classes) {
                 $function_list = implode(', ', array_keys($have_functions));
                 $class_list = implode(', ', array_keys($have_classes));
                 $this->raiseLintInLibrary($library, $file, end($have_functions), self::LINT_ONE_CLASS_PER_FILE, "File '{$file}' mixes function ({$function_list}) and " . "class/interface ({$class_list}) definitions in the same file. " . "A file which declares a class or an interface MUST " . "declare nothing else.");
             } else {
                 if (count($have_classes) > 1) {
                     $class_list = implode(', ', array_keys($have_classes));
                     $this->raiseLintInLibrary($library, $file, end($have_classes), self::LINT_ONE_CLASS_PER_FILE, "File '{$file}' declares more than one class or interface " . "({$class_list}). A file which declares a class or interface MUST " . "declare nothing else.");
                 }
             }
         }
         // Check for duplicate symbols: two files providing the same class or
         // function.
         foreach ($map as $file => $spec) {
             $have = idx($spec, 'have', array());
             foreach (array('class', 'function', 'interface') as $type) {
                 $libtype = $type == 'interface' ? 'class' : $type;
                 foreach (idx($have, $type, array()) as $symbol => $offset) {
                     if (empty($all_symbols[$libtype][$symbol])) {
                         $all_symbols[$libtype][$symbol] = array('library' => $library, 'file' => $file, 'offset' => $offset);
                         continue;
                     }
                     $osrc = $all_symbols[$libtype][$symbol]['file'];
                     $olib = $all_symbols[$libtype][$symbol]['library'];
                     $this->raiseLintInLibrary($library, $file, $offset, self::LINT_DUPLICATE_SYMBOL, "Definition of {$type} '{$symbol}' in '{$file}' in library " . "'{$library}' duplicates prior definition in '{$osrc}' in " . "library '{$olib}'.");
                 }
             }
         }
     }
     foreach ($symbols as $library => $map) {
         // Check for unknown symbols: uses of classes, functions or interfaces
         // which are not defined anywhere. We reference the list of all symbols
         // we built up earlier.
         foreach ($map as $file => $spec) {
             $need = idx($spec, 'need', array());
             foreach (array('class', 'function', 'interface') as $type) {
                 $libtype = $type == 'interface' ? 'class' : $type;
                 foreach (idx($need, $type, array()) as $symbol => $offset) {
                     if (!empty($all_symbols[$libtype][$symbol])) {
                         // Symbol is defined somewhere.
                         continue;
                     }
                     $this->raiseLintInLibrary($library, $file, $offset, self::LINT_UNKNOWN_SYMBOL, "Use of unknown {$type} '{$symbol}'. This symbol is not defined " . "in any loaded phutil library.");
                 }
             }
         }
     }
 }
Exemplo n.º 20
0
#!/usr/bin/env php
<?php 
// NOTE: This is substantially the same as the libphutil/ "launch_daemon.php"
// script, except it loads the Phabricator environment and adds some Phabricator
// specific flags.
declare (ticks=1);
$root = dirname(dirname(dirname(__FILE__)));
require_once $root . '/scripts/__init_script__.php';
$overseer = new PhutilDaemonOverseer($argv);
$bootloader = PhutilBootloader::getInstance();
foreach ($bootloader->getAllLibraries() as $library) {
    $overseer->addLibrary(phutil_get_library_root($library));
}
$overseer->run();
Exemplo n.º 21
0
 /**
  * @task internal
  */
 private function loadClassOrInterfaceSymbol(array $symbol_spec)
 {
     $name = $symbol_spec['name'];
     if (class_exists($name, false) || interface_exists($name, false)) {
         return;
     }
     $bootloader = PhutilBootloader::getInstance();
     $bootloader->loadModule($symbol_spec['library'], $symbol_spec['module']);
     if (!class_exists($name, false) && !interface_exists($name, false)) {
         throw new PhutilMissingSymbolException($name);
     }
 }
 public static function getLibraryVersions()
 {
     $libinfo = array();
     $bootloader = PhutilBootloader::getInstance();
     foreach ($bootloader->getAllLibraries() as $library) {
         $root = phutil_get_library_root($library);
         $try_paths = array($root, dirname($root));
         $libinfo[$library] = array();
         $get_refs = array('master');
         foreach ($try_paths as $try_path) {
             // Try to read what the HEAD of the repository is pointed at. This is
             // normally the name of a branch ("ref").
             $try_file = $try_path . '/.git/HEAD';
             if (@file_exists($try_file)) {
                 $head = @file_get_contents($try_file);
                 $matches = null;
                 if (preg_match('(^ref: refs/heads/(.*)$)', trim($head), $matches)) {
                     $libinfo[$library]['head'] = trim($matches[1]);
                     $get_refs[] = trim($matches[1]);
                 } else {
                     $libinfo[$library]['head'] = trim($head);
                 }
                 break;
             }
         }
         // Try to read which commit relevant branch heads are at.
         foreach (array_unique($get_refs) as $ref) {
             foreach ($try_paths as $try_path) {
                 $try_file = $try_path . '/.git/refs/heads/' . $ref;
                 if (@file_exists($try_file)) {
                     $hash = @file_get_contents($try_file);
                     if ($hash) {
                         $libinfo[$library]['ref.' . $ref] = substr(trim($hash), 0, 12);
                         break;
                     }
                 }
             }
         }
         // Look for extension files.
         $custom = @scandir($root . '/extensions/');
         if ($custom) {
             $count = 0;
             foreach ($custom as $custom_path) {
                 if (preg_match('/\\.php$/', $custom_path)) {
                     $count++;
                 }
             }
             if ($count) {
                 $libinfo[$library]['custom'] = $count;
             }
         }
     }
     ksort($libinfo);
     return $libinfo;
 }