2012-11-22 29 views
4

如何檢查一個對象是否會成功實例化給定的參數,而不實際創建實例?快速檢查一個對象是否會在PHP中成功實例化?

其實我只是檢查(沒有測試此代碼,而應該很好地工作......)的需要參數的數量,忽略類型:

// Filter definition and arguments as per configuration 
$filter = $container->getDefinition($serviceId); 
$args = $activeFilters[$filterName]; 

// Check number of required arguments vs arguments in config 
$constructor = $reflector->getConstructor(); 

$numRequired = $constructor->getNumberOfRequiredParameters(); 
$numSpecified = is_array($args) ? count($args) : 1; 

if($numRequired < $numSpecified) { 
    throw new InvalidFilterDefinitionException(
     $serviceId, 
     $numRequired, 
     $numSpecified 
    ); 
} 

編輯$constructor可以是null ...

+1

它是PHP,當您使用Reflection時,很明顯它是PHP 5.如果您關注*特定PHP 5版本,請使用該PHP版本的標籤 - 但我沒有看到你需要你的問題。 - 你見過http://www.php.net/manual/en/reflectionfunctionabstract.getparameters.php和http://www.php.net/manual/en/class.reflectionparameter.php嗎? – hakre

+0

@hakre很好,對於恢復您的編輯感到抱歉! – gremo

+0

如果不執行代碼,您幾乎不知道實例化是否會成功。在那裏可能會有一個'if(rand())throw new Exception',這使得無需運行代碼就無法確定。 – deceze

回答

5

簡短的答案是,你根本無法確定一組參數是否允許構造函數無錯實例化。正如評論者在上面提到的那樣,無法確切知道某個類是否可以使用給定的參數列表進行實例化,因爲存在運行時注意事項,但實際上並未嘗試實例化。

但是,嘗試使用來從構造函數參數列表中實例化一個類。這種操作最明顯的用例是一個可配置的依賴注入容器(DIC)。不幸的是,這比OP建議的操作複雜得多。

我們需要爲提供的定義數組中的每個參數確定它是否與構造方法簽名中的指定類型提示匹配(如果方法簽名實際上具有類型提示)。另外,我們需要解決如何處理默認參數值。另外,爲了使我們的代碼具有任何實際的用途,我們需要提前指定「定義」來實例化一個類。對問題的複雜處理還將涉及一系列反射對象(緩存),以最大限度地減少反覆反映事件的性能影響。

另一個障礙是,沒有辦法來訪問一個反映方法參數的類型提示,而不調用其ReflectionParameter::getClass方法,並隨後從返回的類名實例化一個反射類(如果返回null帕拉姆沒有類型-暗示)。這是緩存生成的反射對於任何真實世界的用例變得特別重要。

下面的代碼是我自己的基於字符串的遞歸依賴注入容器的嚴格精簡版本。它是僞代碼和實際代碼的混合體(如果您希望免費代碼複製/粘貼,那麼您運氣不佳)。您將看到下面的代碼將「定義」數組的關聯數組鍵與構造函數簽名中的參數名稱進行匹配。

真實的代碼可以在相關的github project page找到。

class Provider { 

    private $definitions; 

    public function define($class, array $definition) { 
     $class = strtolower($class); 
     $this->definitions[$class] = $definition; 
    } 

    public function make($class, array $definition = null) { 
     $class = strtolower($class); 

     if (is_null($definition) && isset($this->definitions[$class])) { 
      $definition = $this->definitions[$class]; 
     } 

     $reflClass = new ReflectionClass($class); 
     $instanceArgs = $this->buildNewInstanceArgs($reflClass); 


     return $reflClass->newInstanceArgs($instanceArgs); 
    } 

    private function buildNewInstanceArgs(
     ReflectionClass $reflClass, 
     array $definition 
    ) { 
     $instanceArgs = array(); 


     $reflCtor = $reflClass->getConstructor(); 

     // IF no constructor exists we're done and should just 
     // return a new instance of $class: 
     // return $this->make($reflClass->name); 
     // otherwise ... 

     $reflCtorParams = $reflCtor->getParameters(); 

     foreach ($reflCtorParams as $ctorParam) { 
      if (isset($definition[$ctorParam->name])) { 
       $instanceArgs[] = $this->make($definition[$ctorParam->name]); 
       continue; 
      } 

      $typeHint = $this->getParameterTypeHint($ctorParam); 

      if ($typeHint && $this->isInstantiable($typeHint)) { 
       // The typehint is instantiable, go ahead and make a new 
       // instance of it 
       $instanceArgs[] = $this->make($typeHint); 
      } elseif ($typeHint) { 
       // The typehint is abstract or an interface. We can't 
       // proceed because we already know we don't have a 
       // definition telling us which class to instantiate 
       throw Exception; 
      } elseif ($ctorParam->isDefaultValueAvailable()) { 
       // No typehint, try to use the default parameter value 
       $instanceArgs[] = $ctorParam->getDefaultValue(); 
      } else { 
       // If all else fails, try passing in a NULL or something 
       $instanceArgs[] = NULL; 
      } 
     } 

     return $instanceArgs; 
    } 

    private function getParameterTypeHint(ReflectionParameter $param) { 
     // ... see the note about retrieving parameter typehints 
     // in the exposition ... 
    } 
    private function isInstantiable($class) { 
     // determine if the class typehint is abstract/interface 
     // RTM on reflection for how to do this 
    } 
} 
相關問題