web-dev-qa-db-ja.com

PHPで実行時に動的にクラスを生成しますか?

これが私がやりたいことです:

$clsName = substr(md5(Rand()),0,10); //generate a random name
$cls = new $clsName(); //create a new instance

function __autoload($class_name)
{
  //define that instance dynamically
}

明らかにこれは実際に行っていることではありませんが、基本的にはクラスに不明な名前があり、その名前に基づいて、特定のプロパティなどを持つクラスを生成します。

私はeval()を使用してみましたが、プライベートおよび$ this->参照よりも適合しています...

//編集

わかりました、明らかに、私の短くて甘い「ここで私がしたいこと」は、答えを提供できるかもしれない人々の間で激しい争いと驚きを引き起こしました。実際の答えが得られることを期待して、詳しく説明します。

維持しているサイトにコードヒントを使用した検証フレームワークがあります。各関数には2つの定義があります

function DoSomething($param, $param2){
   //code
}
function DoSomething_Validate(vInteger $param, vFloat $param2){
   //return what to do if validation fails
}

データベースに主キーのバリデーターを追加しようとしています。 EVERYテーブル(203)に個別のクラスを作成したくありません。だから私の計画は次のようなことをすることでした

function DoSomething_Validate(vPrimaryKey_Products $id){ }

__autoloadがvPrimaryKeyのサブクラスを生成し、テーブルパラメータをProductsに設定する場所。

今幸せです?

28
Will Shaver

これはほぼ間違いなく悪い考えです。

あなたの時間は、クラス定義を作成するスクリプトを作成して、実行時に作成しようとしないほうがよいと思います。

次のようなコマンドライン署名のあるもの:

./generate_classes_from_db <Host> <database> [tables] [output dir]
3
hobodave

面白いことに、これはevalがそれほど悪い考えとは思えない数少ないものの1つです。

ユーザー入力がevalに入らないことを確認できる限り。

コードがキャッシュされないバイトコードキャッシュを使用するときなどの欠点がありますが、evalのセキュリティ問題は、evalにユーザー入力があるか、誤ったスコープになってしまうことにかなり関連しています。

あなたが何をしているのか知っているなら、evalはこれを手助けします。

そうは言っても、私の意見では、検証に型ヒントを使用しない方がはるかに良いですが、1つの関数があります。

DoSomething_Validate($id)
{ 
   // get_class($id) and other validation foo here
}
12
foobaer

私はこれが古い質問であり、うまくいく答えがあることを知っていますが、元の質問に答えるいくつかのスニペットを提供したかったので、誰かが答えを検索したときのように、ここで誰かがここに行き着いたら、より拡張されたソリューションを提供すると思いますこの問題に。

単一の動的クラスを作成

<?php
// Without properties
$myclassname = "anewclassname";
eval("class {$myclassname} { }";
// With a property
$myclassname = "anewclassname";
$myproperty = "newproperty";
eval("class {$myclassname} { protected \${$myproperty}; }";
?>

テキストを適切にエスケープする限り、そこに関数を追加することもできます。

しかし、元の質問で述べたように、データベース内の各テーブルのクラスを作成するなど、それ自体が動的である可能性があるものに基づいてクラスを動的に作成したい場合はどうでしょうか。

複数の動的クラスを作成する

<?php

// Assumes $dbh is a pdo connection handle to your MySQL database
$stmt=$dbh->prepare("show tables");
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$handle = null;
$classcode = '';
foreach ($result as $key => $value) {
    foreach ($value as $key => $value) {
        $classcode = "class {$value} { ";
        $stmt2=$dbh->prepare("DESC $value");
        $stmt2->execute();
        $result2 = $stmt2->fetchAll(PDO::FETCH_ASSOC);
        foreach ($result2 as $key => $value) {
            $classcode .= "public \${$value['Field']}; ";
        }
        $classcode .=  "}";
        eval($classcode);
    }
}

?>

これにより、データベース内の各テーブルのクラスが動的に生成されます。クラスごとに、各列にちなんで名付けられたプロパティも作成されます。

現在、これを行うべきではないと指摘されています。評価で何が起こっているかを制御する限り、セキュリティのリスクは問題ではありません。しかし、あなたがそれについて十分に深く考えれば、より意味のある解決策が最もありそうです。新しいクラスを動的に作成するための完璧なユースケースがあると思いました。問題を注意深く調べると、そうでないことが判明した。

解決策の1つとして、stdClassを使用して、データコンテナーであり、メソッドを必要としないオブジェクトを作成します。

また、前述のように、スクリプトを使用して多数のクラスを手動で生成できます。データベーステーブルをミラーリングするクラスの場合、上記と同じロジックを使用して、evalを実行する代わりに、その情報をファイルに書き込むことができます。

10
akincer

eval()の使用は、特にスクリプトやソフトウェアが別のクライアントに配布される場合は、信頼できる解決策ではないと思います。共有ホスティングプロバイダーは常にeval()関数を無効にします。

私はこのようなより良いアプローチを考えています:

<?php 

 function __autoload( $class ) {
      require 'classes/'.$class.'.php';

}

$class = 'App';
$code = "<?php class $class {
    public function run() {
        echo '$class<br>';  
    }
    ".'
    public function __call($name,$args) {
        $args=implode(",",$args);
        echo "$name ($args)<br>";
    }
}';

file_put_contents('classes/App.php' ,$code);

$a = new $class();
$a->run();

コードの実行が完了したら、必要に応じてファイルを削除できます。テストしたところ、問題なく動作しました。

5

PHP 7.0以降、あまり知られていませんPHP機能のいくつかの創造性と知識があれば、動的にevalやスクリプトファイルを作成することなく、これを行うことができます。 anonymous classesclass_alias ()を次のように使用するだけです。

spl_autoload_register(function ($unfoundClassName) {
{
    $newClass = new class{}; //create an anonymous class
    $newClassName = get_class($newClass); //get the name PHP assigns the anonymous class
    class_alias($newClassName, $unfoundClassName); //alias the anonymous class with your class name
}

これが機能するのは、匿名クラスにバックグラウンドで名前が割り当てられ、グローバルスコープに入れられるためです。そのため、そのクラス名を自由に取得してエイリアスを付けることができます。詳細については、上記の匿名クラスリンクの下の2番目のコメントを確認してください。

そうは言っても、この質問で「Evalは常に非常に悪い考えです。決して使用しないでください!」と言っている人々のように感じます。彼らはHiveの心から聞いたことを繰り返しているだけで、自分で考えているのではありません。 Evalには理由があるため、効果的に使用できる場合があります。古いバージョンのPHPを使用している場合は、evalが良い解決策になるかもしれません。

ただしできる非常に大きなセキュリティホールを開いて、それをどのように使用するかに注意し、リスクを排除する方法を理解する必要があります。重要なことは、SQLインジェクションと同様に、evalステートメントに入力したすべての入力を無害化する必要があることです。

たとえば、オートローダーが次のようになっているとします。

spl_autoload_register(function ($unfoundClassName) {
{
    eval("class $unfoundClassName {}");
}

ハッカーは次のようなことをすることができます:

$injectionCode = "bogusClass1{} /*insert malicious code to run on the server here */ bogusClass2";

new $injectionCode();

これがどのようにセキュリティホールになる可能性があるかを見てください。 ハッカーが2つのbogusClass名の間に置いたものは、evalステートメントによってサーバー上で実行されます。

渡されたクラス名をチェックするようにオートローダーを調整する場合(つまり、スペースや特殊文字がないことを確認するためにpreg_matchを実行する、受け入れ可能な名前のリストと照合するなど)、これらのリスクを排除すると、evalがこの状況で使用してもまったく問題ありません。 PHP 7以上を使用している場合は、上記の匿名クラスエイリアスメソッドをお勧めします。

3
dallin

Eval()を使用するのは本当に悪い考えです。大きなセキュリティホールが開かれます。使用しないでください。

3
function __autoload($class)  {
    $code = "class $class {`
        public function run() {
            echo '$class<br>';  
        }
        ".'
        public function __call($name,$args) {
            $args=implode(",",$args);
            echo "$name ($args)<br>";
        }
    }';

    eval($code);
}

$app=new Klasse();
$app->run();
$app->HelloWorld();

これは、実行時にクラスを作成するのに役立ちます。また、未知のメソッドに対してメソッド実行とキャッチオールメソッドを作成しますが、クラスではなくオブジェクトを実行時に作成する方が適切です。

3
Matthias

これが本当に非常に悪い考えである方法に関する他のすべての回答を読んでください。

それが理解できたら、これを実行する方法についての小さなデモを以下に示します。


<?php
$clname = "TestClass";

eval("class $clname{}; \$cls = new $clname();");

var_dump($cls);
2
MitMaro

クラス/インターフェイス/特性を動的に作成するパッケージを作成し、それらをファイルに保存します。作成したファイルを含めるだけで、クラスを使用できます。

パッケージ: https://packagist.org/packages/kanel/enuma

0
Adil El Kanabi