2009-05-30 4 views
34

プロジェクト内の各PHPファイルに単一のクラス定義が含まれているとすれば、ファイル内で定義されているクラスを確認するにはどうすればよいですか?PHPクラスファイルで定義されているクラスの決定

私はclass文のファイルを正規表現することができますが、もっと効率的な方法をとっています。

+0

各ファイルのクラス名を取得する目的は何ですか?最適なソリューションは、問題のスペースに合わせて調整する必要があります。それが目立つように、あなたが探しているものに応じて、おそらくもっと良い解決策があるように感じます。 –

+4

さっそくですが、 'get_declared_classes'を呼び出して保存し、クラスファイルをインクルードし、' get_declared_classes'をもう一度呼び出すことができます。違いはそのファイルにあります。シンプル。 – Rudie

答えて

56

私はここに私が働いているプロジェクトのために、このような何かを必要とし、私が書いた関数です:

ここ

は私c2.phpです
function file_get_php_classes($filepath) { 
    $php_code = file_get_contents($filepath); 
    $classes = get_php_classes($php_code); 
    return $classes; 
} 

function get_php_classes($php_code) { 
    $classes = array(); 
    $tokens = token_get_all($php_code); 
    $count = count($tokens); 
    for ($i = 2; $i < $count; $i++) { 
    if ( $tokens[$i - 2][0] == T_CLASS 
     && $tokens[$i - 1][0] == T_WHITESPACE 
     && $tokens[$i][0] == T_STRING) { 

     $class_name = $tokens[$i][1]; 
     $classes[] = $class_name; 
    } 
    } 
    return $classes; 
} 
+0

すべてのトークンが配列であるわけではないので、いくつかの警告が出る可能性があります。 – hakre

+0

@hakre: '$ tokens [$ i]'が文字列であれば、 '$ tokens [i] [0]'の構文は許されているからです。 – netcoder

+0

そうですが、その場合の比較に '==='を使うことを提案します。 – hakre

2

PHPの関数get_declared_classes()を使用してください。これは、現在のスクリプトで定義されたクラスの配列を返します。

+0

これをインクルードの前のクラスのリストと比較する必要があります。もっと効率的なものがあると思いますか? –

+0

インクルードでファイルをロードする場合は、これが私が考える最も効率的な方法です。 –

+1

ファイルに含まれるクラスを確認したい場合、これは行いません。インクルード/オートローディング/必須のファイルがすでに含まれている可能性があるため、インクルードの前後でget_declared_classes()を比較することはできません。 – cletus

15

あなたはちょうどそれがtoken_get_all()を使用するロードせずにファイルをチェックしたい場合:

<?php 
header('Content-Type: text/plain'); 
$php_file = file_get_contents('c2.php'); 
$tokens = token_get_all($php_file); 
$class_token = false; 
foreach ($tokens as $token) { 
    if (is_array($token)) { 
    if ($token[0] == T_CLASS) { 
     $class_token = true; 
    } else if ($class_token && $token[0] == T_STRING) { 
     echo "Found class: $token[1]\n"; 
     $class_token = false; 
    } 
    }  
} 
?> 

基本的に、これは単純な有限状態機械です。 PHPでは、tokensのシーケンスは、

  • T_CLASS: 'class'キーワードです。
  • T_WHITESPACE: 'class'の後のスペース。
  • T_STRING:クラス名。

このコードでは、ファイルを実行するためにPHPが使用するのと同じパーサを使用しているため、奇妙な間隔や改行を処理できます。 token_get_all()が解析できない場合、どちらもPHPになりません。

ところで、token_name()を使用して、トークン番号を定数名に変換します。

<?php 
class MyClass { 
    public __construct() { 
    } 
} 

class MyOtherClass { 
    public __construct() { 
    } 
} 
?> 

出力:

Found class: MyClass 
Found class: MyOtherClass 
+0

'User :: class'のようなものは、PHP 5.5からFQCN文字列(" App \ Model \ User "のような)を検索するのに有効な構文です。したがって、その間にT_WHITESPACE(と他に何もない)を明示的にチェックするか、T_CLASSの前にT_COLONがないかチェックできます。 – Fx32

1

私はVenkat Dの答えを少し戻してメソッドを返すようにし、ディレクトリを検索しました。 (この具体的な例は、./system/application/controllerファイル内のすべてのメソッドを返しますCodeIgniterの、のために構築されている - つまり、あなたがシステムを通じて呼び出すことができるすべてのパブリックURL。)

function file_get_php_classes($filepath,$onlypublic=true) { 
    $php_code = file_get_contents($filepath); 
    $classes = get_php_classes($php_code,$onlypublic); 
    return $classes; 
} 

function get_php_classes($php_code,$onlypublic) { 
    $classes = array(); 
    $methods=array(); 
    $tokens = token_get_all($php_code); 
    $count = count($tokens); 
    for ($i = 2; $i < $count; $i++) { 
     if ($tokens[$i - 2][0] == T_CLASS 
     && $tokens[$i - 1][0] == T_WHITESPACE 
     && $tokens[$i][0] == T_STRING) { 
      $class_name = $tokens[$i][1]; 
      $methods[$class_name] = array(); 
     } 
     if ($tokens[$i - 2][0] == T_FUNCTION 
     && $tokens[$i - 1][0] == T_WHITESPACE 
     && $tokens[$i][0] == T_STRING) { 
      if ($onlypublic) { 
       if (!in_array($tokens[$i-4][0],array(T_PROTECTED, T_PRIVATE))) { 
        $method_name = $tokens[$i][1]; 
        $methods[$class_name][] = $method_name; 
       } 
      } else { 
       $method_name = $tokens[$i][1]; 
       $methods[$class_name][] = $method_name; 
      } 
     } 
    } 
    return $methods; 
} 

function mapSystemClasses($controllerdir="./system/application/controllers/",$onlypublic=true) { 
    $result=array(); 
    $dh=opendir($controllerdir); 
    while (($file = readdir($dh)) !== false) { 
     if (substr($file,0,1)!=".") { 
      if (filetype($controllerdir.$file)=="file") { 
       $classes=file_get_php_classes($controllerdir.$file,$onlypublic); 
       foreach($classes as $class=>$method) { 
        $result[]=array("file"=>$controllerdir.$file,"class"=>$class,"method"=>$method); 

       } 
      } else { 
       $result=array_merge($result,mapSystemClasses($controllerdir.$file."/",$onlypublic)); 
      } 
     } 
    } 
    closedir($dh); 
    return $result; 
} 
0

ます私は名前空間を持つファイルから解析クラスを必要と

function get_php_classes($php_code) 
{ 
    $classes = array(); 
    $tokens = token_get_all($php_code); 
    $count = count($tokens); 
    for ($i = 2; $i < $count; $i++) 
    { 
     if ($tokens[$i - 2][0] == T_CLASS && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING && !($tokens[$i - 3] && $i - 4 >= 0 && $tokens[$i - 4][0] == T_ABSTRACT)) 
     { 
      $class_name = $tokens[$i][1]; 
      $classes[] = $class_name; 
     } 
    } 
    return $classes; 
} 
+1

これは、カウントが2から始まり、負の数が正当な配列キーではないため、Illegal Offsetを発生させます。 (2-4 = -2)... T_ABSTRACTをテストする前に条件$ i-4> = 0を追加すると、これを修正できます。 – Tivie

5

ので、私は、コードを変更:このように抽象クラスを無視することができます(T_ABSTRACTトークンを注意してください)。もしも誰かが必要なら、ここにあります:

public function getPhpClasses($phpcode) { 
    $classes = array(); 

    $namespace = 0; 
    $tokens = token_get_all($phpcode); 
    $count = count($tokens); 
    $dlm = false; 
    for ($i = 2; $i < $count; $i++) { 
     if ((isset($tokens[$i - 2][1]) && ($tokens[$i - 2][1] == "phpnamespace" || $tokens[$i - 2][1] == "namespace")) || 
      ($dlm && $tokens[$i - 1][0] == T_NS_SEPARATOR && $tokens[$i][0] == T_STRING)) { 
      if (!$dlm) $namespace = 0; 
      if (isset($tokens[$i][1])) { 
       $namespace = $namespace ? $namespace . "\\" . $tokens[$i][1] : $tokens[$i][1]; 
       $dlm = true; 
      } 
     }  
     elseif ($dlm && ($tokens[$i][0] != T_NS_SEPARATOR) && ($tokens[$i][0] != T_STRING)) { 
      $dlm = false; 
     } 
     if (($tokens[$i - 2][0] == T_CLASS || (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] == "phpclass")) 
       && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING) { 
      $class_name = $tokens[$i][1]; 
      if (!isset($classes[$namespace])) $classes[$namespace] = array(); 
      $classes[$namespace][] = $class_name; 
     } 
    } 
    return $classes; 
} 
+0

複数の名前空間を持つファイルでスニペットを動作させることができませんでした – Tivie

3

私のスニペットも。複数のクラス、インタフェース、配列、名前空間を持つファイルを解析できます。 クラス+型(クラス、インタフェース、抽象)を名前空間で割った配列を返します。

<?php  
    /** 
    * 
    * Looks what classes and namespaces are defined in that file and returns the first found 
    * @param String $file Path to file 
    * @return Returns NULL if none is found or an array with namespaces and classes found in file 
    */ 
    function classes_in_file($file) 
    { 

     $classes = $nsPos = $final = array(); 
     $foundNS = FALSE; 
     $ii = 0; 

     if (!file_exists($file)) return NULL; 

     $er = error_reporting(); 
     error_reporting(E_ALL^E_NOTICE); 

     $php_code = file_get_contents($file); 
     $tokens = token_get_all($php_code); 
     $count = count($tokens); 

     for ($i = 0; $i < $count; $i++) 
     { 
      if(!$foundNS && $tokens[$i][0] == T_NAMESPACE) 
      { 
       $nsPos[$ii]['start'] = $i; 
       $foundNS = TRUE; 
      } 
      elseif($foundNS && ($tokens[$i] == ';' || $tokens[$i] == '{')) 
      { 
       $nsPos[$ii]['end']= $i; 
       $ii++; 
       $foundNS = FALSE; 
      } 
      elseif ($i-2 >= 0 && $tokens[$i - 2][0] == T_CLASS && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING) 
      { 
       if($i-4 >=0 && $tokens[$i - 4][0] == T_ABSTRACT) 
       { 
        $classes[$ii][] = array('name' => $tokens[$i][1], 'type' => 'ABSTRACT CLASS'); 
       } 
       else 
       { 
        $classes[$ii][] = array('name' => $tokens[$i][1], 'type' => 'CLASS'); 
       } 
      } 
      elseif ($i-2 >= 0 && $tokens[$i - 2][0] == T_INTERFACE && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING) 
      { 
       $classes[$ii][] = array('name' => $tokens[$i][1], 'type' => 'INTERFACE'); 
      } 
     } 
     error_reporting($er); 
     if (empty($classes)) return NULL; 

     if(!empty($nsPos)) 
     { 
      foreach($nsPos as $k => $p) 
      { 
       $ns = ''; 
       for($i = $p['start'] + 1; $i < $p['end']; $i++) 
        $ns .= $tokens[$i][1]; 

       $ns = trim($ns); 
       $final[$k] = array('namespace' => $ns, 'classes' => $classes[$k+1]); 
      } 
      $classes = $final; 
     } 
     return $classes; 
    } 

出力は次のようなものです。

array 
    'namespace' => string 'test\foo' (length=8) 
    'classes' => 
    array 
     0 => 
     array 
      'name' => string 'bar' (length=3) 
      'type' => string 'CLASS' (length=5) 
     1 => 
     array 
      'name' => string 'baz' (length=3) 
      'type' => string 'INTERFACE' (length=9) 
array 
    'namespace' => string 'this\is\a\really\big\namespace\for\testing\dont\you\think' (length=57) 
    'classes' => 
    array 
     0 => 
     array 
      'name' => string 'yes_it_is' (length=9) 
      'type' => string 'CLASS' (length=5) 
     1 => 
     array 
      'name' => string 'damn_too_big' (length=12) 
      'type' => string 'ABSTRACT CLASS' (length=14) 
     2 => 
     array 
      'name' => string 'fodass' (length=6) 
      'type' => string 'INTERFACE' (length=9) 

誰かが役に立つかもしれない!

4

それとも、簡単に(作曲を使用してインストール可能)からAnnotationsParserを使用することができます。

array(1) { 
    ["Your\Class\Name"] => 
    array(...) { 
     // property => comment 
    }, 
    ["Your\Class\Second"] => 
    array(...) { 
     // property => comment 
    }, 
} 

parsePhp() method基本的に他の例として、似たようなことを行います。

use Nette\Reflection\AnnotationsParser; 
$classes = AnnotationsParser::parsePhp(file_get_contents($fileName)); 
var_dump($classes); 

出力は、このような何かがされますあなたはあなた自身の構文解析を宣言したりテストしたりする必要はありません。

関連する問題