Динамически генерировать классы во время выполнения в php? - PullRequest
28 голосов
/ 30 июля 2009

Вот что я хочу сделать:

$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-> ссылкам ...

// редактировать

Хорошо, очевидно, что мое короткое и милое «вот что я хочу сделать» вызвало массовые волнения и смятение среди тех, кто может быть в состоянии дать ответы. В надежде получить реальный ответ я буду более подробно.

У меня есть структура проверки с использованием подсказок кода на сайте, который я поддерживаю. Каждая функция имеет два определения

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

Я хочу добавить валидатор для первичных ключей в моей базе данных. Я не хочу создавать отдельный класс для КАЖДОЙ таблицы (203). Поэтому я планировал сделать что-то вроде

function DoSomething_Validate(vPrimaryKey_Products $id){ }

Где __autoload сгенерирует подкласс vPrimaryKey и установит для параметра таблицы значение Products.

Счастлив теперь?

Ответы [ 9 ]

12 голосов
/ 03 июня 2010

забавно, на самом деле это одна из немногих вещей, где eval не кажется такой плохой идеей.

, пока вы можете быть уверены, что пользовательский ввод никогда не введет eval.

у вас все еще есть недостатки, как при использовании кеша байт-кода, когда код не будет кэшироваться и т. Д. И т. Д., Но проблемы безопасности eval в значительной степени связаны с вводом пользователя в eval или с неправильной областью действия

если вы знаете, что делаете, eval поможет вам в этом.

Тем не менее, по моему мнению, вам гораздо лучше, когда вы не полагаетесь на подсказки типов при проверке, но у вас есть одна функция

DoSomething_Validate($id)
{ 
   // get_class($id) and other validation foo here
}
10 голосов
/ 23 марта 2013

Я знаю, что это старый вопрос, и есть ответы, которые БУДУТ работать, но я хотел предложить несколько фрагментов, которые ответили бы на исходный вопрос, и я думаю, предложить более расширенное решение, если кто-то окажется здесь, как я, когда искал за ответ на эту проблему.

Создание одного динамического класса

<?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);
    }
}

?>

Это будет динамически генерировать класс для каждой таблицы в базе данных. Также для каждого класса будет создано свойство, названное в честь каждого столбца.

Теперь было указано, что вы не должны этого делать. Пока вы контролируете, что происходит в eval, риск безопасности не является проблемой. НО - скорее всего, есть решение, которое имеет больше смысла, если вы достаточно глубоко об этом думаете. Я думал, что у меня есть идеальный вариант использования для динамического создания новых классов. Тщательное изучение проблемы доказало обратное.

Одно из возможных решений - использовать stdClass для создания объектов, которые являются просто контейнерами данных и не нуждаются в каких-либо методах.

Также - как уже упоминалось, вы можете использовать скрипт для генерации вручную большого количества классов. В случае классов, отражающих таблицы вашей базы данных, вы можете использовать ту же логику, которая была у меня выше, и вместо выполнения eval записать эту информацию в файл.

5 голосов
/ 30 августа 2014

Я думаю, что использование 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();

После завершения выполнения кода вы можете удалить файл, если хотите, я протестировал его, и он отлично работает.

3 голосов
/ 17 октября 2018

Начиная с PHP 7.0, с небольшим творческим потенциалом и знанием некоторых менее известных функций PHP, вы можете абсолютно сделать это, не прибегая к eval или созданию файлов сценариев динамически. Вам просто нужно использовать анонимные классы и class_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
}

Это работает, потому что анонимным классам по-прежнему присваивается имя за кулисами, и они помещаются в глобальную область видимости, поэтому вы можете свободно выбирать имя класса и псевдоним его. Посмотрите второй комментарий по ссылке анонимных классов выше для получения дополнительной информации.

Сказав это, я чувствую, что все люди в этом вопросе, которые говорят: «Эвал - всегда очень плохая идея. Просто не используйте ее никогда!» просто повторяют то, что слышали от улья, и не думают сами за себя. 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();

Видите, как это может стать дырой в безопасности? Все, что угодно , которое хакер поместит между двумя именами bogusClass, будет запущено на вашем сервере оператором eval.

Если вы настроите свой автозагрузчик для проверки переданного имени класса (т.е. выполните preg_match, чтобы убедиться, что нет пробелов или специальных символов, сверяете его со списком допустимых имен и т. Д.), Вы можете устранить эти риски и затем Eval может быть совершенно нормально использовать в этой ситуации. Если вы используете PHP 7 или выше, я рекомендую использовать анонимный метод псевдонима класса выше.

3 голосов
/ 18 марта 2012
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 голосов
/ 30 июля 2009

Использование eval () действительно плохая идея. Это открывает большую дыру в безопасности. Только не используйте его!

3 голосов
/ 30 июля 2009

Это почти наверняка плохая идея.

Я думаю, что ваше время было бы лучше потратить на создание сценария, который бы создал для вас определения классов, а не на попытки сделать это во время выполнения.

Что-то с подписью командной строки, например:

./generate_classes_from_db <host> <database> [tables] [output dir]
2 голосов
/ 30 июля 2009

Пожалуйста, прочитайте ответы всех остальных о том, как это действительно очень, очень плохая идея.

Как только вы это поймете, вот небольшая демонстрация того, как вы можете, но не должны делать это.


<?php
$clname = "TestClass";

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

var_dump($cls);
0 голосов
/ 22 июня 2018

Я создал пакет, который динамически создает классы / интерфейсы / признаки ... и сохраняет их в файл затем вы можете просто включить созданный файл, чтобы использовать ваш класс

упаковка: https://packagist.org/packages/kanel/enuma

...