Changeset View
Changeset View
Standalone View
Standalone View
src/symbols/PhutilSymbolLoader.php
Show First 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | final class PhutilSymbolLoader { | ||||
private $type; | private $type; | ||||
private $library; | private $library; | ||||
private $base; | private $base; | ||||
private $name; | private $name; | ||||
private $concrete; | private $concrete; | ||||
private $pathPrefix; | private $pathPrefix; | ||||
private $suppressLoad; | private $suppressLoad; | ||||
private $continueOnFailure; | |||||
/** | /** | ||||
* Select the type of symbol to load, either `class`, `function` or | * Select the type of symbol to load, either `class`, `function` or | ||||
* `interface`. | * `interface`. | ||||
* | * | ||||
* @param string Type of symbol to load. | * @param string Type of symbol to load. | ||||
* @return this | * @return this | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | final class PhutilSymbolLoader { | ||||
* | * | ||||
* @task config | * @task config | ||||
*/ | */ | ||||
public function setConcreteOnly($concrete) { | public function setConcreteOnly($concrete) { | ||||
$this->concrete = $concrete; | $this->concrete = $concrete; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function setContinueOnFailure($continue) { | |||||
$this->continueOnFailure = $continue; | |||||
return $this; | |||||
} | |||||
/* -( Load )--------------------------------------------------------------- */ | /* -( Load )--------------------------------------------------------------- */ | ||||
/** | /** | ||||
* Execute the query and select matching symbols, then load them so they can | * Execute the query and select matching symbols, then load them so they can | ||||
* be used. | * be used. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | foreach ($libraries as $library) { | ||||
'library' => $library, | 'library' => $library, | ||||
'where' => $where, | 'where' => $where, | ||||
); | ); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!$this->suppressLoad) { | if (!$this->suppressLoad) { | ||||
// Loading a class may trigger the autoloader to load more classes | |||||
// (usually, the parent class), so we need to keep track of whether we | |||||
// are currently loading in "continue on failure" mode. Otherwise, we'll | |||||
// fail anyway if we fail to load a parent class. | |||||
// The driving use case for the "continue on failure" mode is to let | |||||
// "arc liberate" run so it can rebuild the library map, even if you have | |||||
// made changes to Workflow or Config classes which it must load before | |||||
// it can operate. If we don't let it continue on failure, it is very | |||||
// difficult to remove or move Workflows. | |||||
static $continue_depth = 0; | |||||
if ($this->continueOnFailure) { | |||||
$continue_depth++; | |||||
} | |||||
$caught = null; | $caught = null; | ||||
foreach ($symbols as $symbol) { | foreach ($symbols as $key => $symbol) { | ||||
try { | try { | ||||
$this->loadSymbol($symbol); | $this->loadSymbol($symbol); | ||||
} catch (Exception $ex) { | } catch (Exception $ex) { | ||||
// If we failed to load this symbol, remove it from the results. | |||||
// Otherwise, we may fatal below when trying to reflect it. | |||||
unset($symbols[$key]); | |||||
$caught = $ex; | $caught = $ex; | ||||
} | } | ||||
} | } | ||||
$should_continue = ($continue_depth > 0); | |||||
if ($this->continueOnFailure) { | |||||
$continue_depth--; | |||||
} | |||||
if ($caught) { | if ($caught) { | ||||
// NOTE: We try to load everything even if we fail to load something, | // NOTE: We try to load everything even if we fail to load something, | ||||
// primarily to make it possible to remove functions from a libphutil | // primarily to make it possible to remove functions from a libphutil | ||||
// library without breaking library startup. | // library without breaking library startup. | ||||
if ($should_continue) { | |||||
// We may not have `pht()` yet. | |||||
fprintf( | |||||
STDERR, | |||||
"%s: %s\n", | |||||
'IGNORING CLASS LOAD FAILURE', | |||||
$caught->getMessage()); | |||||
} else { | |||||
throw $caught; | throw $caught; | ||||
} | } | ||||
} | } | ||||
} | |||||
if ($this->concrete) { | if ($this->concrete) { | ||||
// Remove 'abstract' classes. | // Remove 'abstract' classes. | ||||
foreach ($symbols as $key => $symbol) { | foreach ($symbols as $key => $symbol) { | ||||
if ($symbol['type'] == 'class') { | if ($symbol['type'] == 'class') { | ||||
$reflection = new ReflectionClass($symbol['name']); | $reflection = new ReflectionClass($symbol['name']); | ||||
if ($reflection->isAbstract()) { | if ($reflection->isAbstract()) { | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | private function loadSymbol(array $symbol_spec) { | ||||
$bootloader->loadLibrarySource($lib_name, $where); | $bootloader->loadLibrarySource($lib_name, $where); | ||||
// Check that we successfully loaded the symbol from wherever it was | // Check that we successfully loaded the symbol from wherever it was | ||||
// supposed to be defined. | // supposed to be defined. | ||||
$load_failed = null; | $load_failed = null; | ||||
if ($is_function) { | if ($is_function) { | ||||
if (!function_exists($name)) { | if (!function_exists($name)) { | ||||
$load_failed = 'function'; | $load_failed = pht('function'); | ||||
} | } | ||||
} else { | } else { | ||||
if (!class_exists($name, false) && !interface_exists($name, false)) { | if (!class_exists($name, false) && !interface_exists($name, false)) { | ||||
$load_failed = 'class/interface'; | $load_failed = pht('class or interface'); | ||||
} | } | ||||
} | } | ||||
if ($load_failed !== null) { | if ($load_failed !== null) { | ||||
$lib_path = phutil_get_library_root($lib_name); | $lib_path = phutil_get_library_root($lib_name); | ||||
throw new PhutilMissingSymbolException( | throw new PhutilMissingSymbolException( | ||||
$name, | $name, | ||||
$load_failed, | $load_failed, | ||||
pht( | pht( | ||||
'The symbol map for library "%s" (at "%s") claims this symbol '. | "The symbol map for library '%s' (at '%s') claims this %s is ". | ||||
'(of type "%s") is defined in "%s", but loading that source file '. | "defined in '%s', but loading that source file did not cause the ". | ||||
'did not cause the symbol to become defined.', | "%s to become defined.", | ||||
$lib_name, | $lib_name, | ||||
$lib_path, | $lib_path, | ||||
$load_failed, | $load_failed, | ||||
$where)); | $where, | ||||
$load_failed)); | |||||
} | } | ||||
} | } | ||||
} | } |