Path: blob/master/src/applications/conduit/call/ConduitCall.php
12256 views
<?php12/**3* Run a conduit method in-process, without requiring HTTP requests. Usage:4*5* $call = new ConduitCall('method.name', array('param' => 'value'));6* $call->setUser($user);7* $result = $call->execute();8*9*/10final class ConduitCall extends Phobject {1112private $method;13private $handler;14private $request;15private $user;1617public function __construct($method, array $params, $strictly_typed = true) {18$this->method = $method;19$this->handler = $this->buildMethodHandler($method);2021$param_types = $this->handler->getParamTypes();2223foreach ($param_types as $key => $spec) {24if (ConduitAPIMethod::getParameterMetadataKey($key) !== null) {25throw new ConduitException(26pht(27'API Method "%s" defines a disallowed parameter, "%s". This '.28'parameter name is reserved.',29$method,30$key));31}32}3334$invalid_params = array_diff_key($params, $param_types);35if ($invalid_params) {36throw new ConduitException(37pht(38'API Method "%s" does not define these parameters: %s.',39$method,40"'".implode("', '", array_keys($invalid_params))."'"));41}4243$this->request = new ConduitAPIRequest($params, $strictly_typed);44}4546public function getAPIRequest() {47return $this->request;48}4950public function setUser(PhabricatorUser $user) {51$this->user = $user;52return $this;53}5455public function getUser() {56return $this->user;57}5859public function shouldRequireAuthentication() {60return $this->handler->shouldRequireAuthentication();61}6263public function shouldAllowUnguardedWrites() {64return $this->handler->shouldAllowUnguardedWrites();65}6667public function getErrorDescription($code) {68return $this->handler->getErrorDescription($code);69}7071public function execute() {72$profiler = PhutilServiceProfiler::getInstance();73$call_id = $profiler->beginServiceCall(74array(75'type' => 'conduit',76'method' => $this->method,77));7879try {80$result = $this->executeMethod();81} catch (Exception $ex) {82$profiler->endServiceCall($call_id, array());83throw $ex;84}8586$profiler->endServiceCall($call_id, array());87return $result;88}8990private function executeMethod() {91$user = $this->getUser();92if (!$user) {93$user = new PhabricatorUser();94}9596$this->request->setUser($user);9798if (!$this->shouldRequireAuthentication()) {99// No auth requirement here.100} else {101102$allow_public = $this->handler->shouldAllowPublic() &&103PhabricatorEnv::getEnvConfig('policy.allow-public');104if (!$allow_public) {105if (!$user->isLoggedIn() && !$user->isOmnipotent()) {106// TODO: As per below, this should get centralized and cleaned up.107throw new ConduitException('ERR-INVALID-AUTH');108}109}110111// TODO: This would be slightly cleaner by just using a Query, but the112// Conduit auth workflow requires the Call and User be built separately.113// Just do it this way for the moment.114$application = $this->handler->getApplication();115if ($application) {116$can_view = PhabricatorPolicyFilter::hasCapability(117$user,118$application,119PhabricatorPolicyCapability::CAN_VIEW);120121if (!$can_view) {122throw new ConduitException(123pht(124'You do not have access to the application which provides this '.125'API method.'));126}127}128}129130return $this->handler->executeMethod($this->request);131}132133protected function buildMethodHandler($method_name) {134$method = ConduitAPIMethod::getConduitMethod($method_name);135136if (!$method) {137throw new ConduitMethodDoesNotExistException($method_name);138}139140$application = $method->getApplication();141if ($application && !$application->isInstalled()) {142$app_name = $application->getName();143throw new ConduitApplicationNotInstalledException($method, $app_name);144}145146return $method;147}148149public function getMethodImplementation() {150return $this->handler;151}152153154}155156157