Каковы последствия для производительности, безопасности или «других» последствий использования следующей формы для объявления экземпляра нового класса в PHP?
<?php
$class_name = 'SomeClassName';
$object = new $class_name;
?>
Это надуманный пример, но я видел, как эта форма используется в Factories (OOP), чтобы избежать большого оператора if / switch.
Проблемы, которые сразу приходят в голову:
Какие еще последствия существуют или какие термины поисковых систем, кроме "Rank PHP Hackery", можно использовать для исследования?






Похоже, вы все еще можете передавать аргументы конструктору, вот мой тестовый код:
<?php
class Test {
function __construct($x) {
echo $x;
}
}
$class = 'Test';
$object = new $class('test'); // echoes "test"
?>
Вы ведь это имели в виду?
Итак, единственная другая проблема, о которой вы упомянули и о которой я могу думать, - это ее безопасность, но сделать это не должно быть слишком сложно, и, очевидно, это намного безопаснее, чем использование eval ().
Действительно, может возникнуть снижение производительности из-за необходимости разрешать имя переменной перед поиском определения класса. Но без динамического объявления классов у вас нет реального способа выполнять «динамическое» или «мета» программирование. Вы не сможете писать программы генерации кода или что-то вроде предметно-ориентированной языковой конструкции.
Мы используем это соглашение повсеместно в некоторых основных классах нашей внутренней структуры, чтобы заставить работать сопоставления URL-адресов с контроллерами. Я также видел это во многих коммерческих приложениях с открытым исходным кодом (я попытаюсь найти пример и выложить его). В любом случае, суть моего ответа заключается в том, что, похоже, это стоит того, что, вероятно, будет небольшим снижением производительности, если он сделает более гибкий, динамический код.
Другой компромисс, о котором я должен упомянуть, - это производительность, которая делает код менее очевидным и читаемым, если вы не будете очень осторожны с именами переменных. Большая часть кода пишется один раз и многократно перечитывается и модифицируется, поэтому удобочитаемость важна.
Одна из проблем с разрешением во время выполнения заключается в том, что вы действительно усложняете работу с кешами кодов операций (например, APC). Тем не менее, на данный момент выполнение чего-то вроде того, что вы описываете в своем вопросе, является допустимым способом, если вам нужно определенное количество косвенных указаний при создании экземпляров материала.
Пока вы не делаете что-то вроде
$classname = 'SomeClassName';
for ($x = 0; $x < 100000; $x++){
$object = new $classname;
}
ты наверное в порядке :-)
(Моя точка зрения: динамический поиск класса здесь и затем не повредит. Если вы будете делать это часто, это будет).
Кроме того, убедитесь, что $ classname никогда не может быть установлено извне - вы хотите иметь некоторый контроль над тем, какой именно класс вы будете создавать.
Я использую динамическое создание экземпляров в своей пользовательской структуре. Контроллер моего приложения должен создать экземпляр субконтроллера на основе запроса, и было бы просто нелепо использовать гигантский, постоянно меняющийся оператор switch для управления загрузкой этих контроллеров. В результате я могу добавлять контроллер за контроллером в свое приложение, не изменяя контроллер приложения для их вызова. Пока мои URI соответствуют соглашениям моей платформы, контроллер приложения может использовать их, ничего не зная до времени выполнения.
Я использую этот фреймворк в производственном приложении корзины покупок прямо сейчас, и производительность тоже довольно хорошая. При этом я использую динамический выбор класса только в одном или двух местах всего приложения. Мне интересно, при каких обстоятельствах вам нужно будет использовать его часто, и являются ли эти ситуации теми, которые страдают от желания программиста чрезмерно абстрагировать приложение (я был виноват в этом раньше).
Алан, нет ничего плохого в инициализации динамического класса. Этот метод присутствует также в языке Java, где можно преобразовать строку в класс с помощью метода Class.forClass('classname'). Также очень удобно переносить сложность алгоритма на несколько классов вместо того, чтобы иметь список if-условий. Имена динамических классов особенно хорошо подходят в ситуациях, когда вы хотите, чтобы ваш код оставался открытым для расширения без необходимости внесения изменений.
Я сам часто использую разные классы вместе с таблицами базы данных. В одном столбце я храню имя класса, который будет использоваться для обработки записи. Это дает мне огромную возможность добавлять новые типы записей и обрабатывать их уникальным образом, не изменяя ни единого байта в существующем коде.
Вы не должны беспокоиться о производительности. Он почти не требует накладных расходов, а сами объекты в PHP работают очень быстро. Если вам нужно создать тысячи идентичных объектов, используйте шаблон проектирования Наилегчайший вес, чтобы уменьшить объем памяти. В частности, вы не должны жертвовать своим временем как разработчик только ради экономии миллисекунд на сервере. Также оптимизаторы кода операции без проблем работают с этой техникой. Скрипты, скомпилированные с помощью Zend Optimizer, работали нормально.
Я бы добавил, что вы также можете создать его экземпляр с динамическим числом параметров, используя:
<?php
$class = "Test";
$args = array('a', 'b');
$ref = new ReflectionClass($class);
$instance = $ref->newInstanceArgs($args);
?>
Но, конечно, вы добавляете дополнительные накладные расходы, делая это.
Что касается проблемы безопасности, я не думаю, что это имеет большое значение, по крайней мере, это ничто по сравнению с eval (). В худшем случае создается экземпляр неправильного класса, конечно, это потенциальное нарушение безопасности, но его гораздо труднее использовать, и его легко фильтровать с помощью массива разрешенных классов, если вам действительно нужен ввод пользователя для определения имени класса.
Одна из проблем заключается в том, что вы не можете обращаться к таким статическим членам, например
<?php
$className = 'ClassName';
$className::someStaticMethod(); //doesn't work
?>
call_user_func (массив ($ className, 'someStaticMethod'));
@coldFlame: IIRC, вы можете использовать call_user_func(array($className, 'someStaticMethod') и call_user_func_array() для передачи параметров
class Test {
function testExt() {
print 'hello from testExt :P';
}
function test2Ext()
{
print 'hi from test2Ext :)';
}
}
$class = 'Test';
$method_1 = "testExt";
$method_2 = "test2Ext";
$object = new $class(); // echoes "test"
$object->{$method_2}(); // will print 'hi from test2Ext :)'
$object->{$method_1}(); // will print 'hello from testExt :P';
этот трюк работает как на php4, так и на php5: D наслаждайтесь ..
Итак, я недавно столкнулся с этим и хотел поделиться своими мыслями о «других» последствиях использования динамического создания экземпляров.
Во-первых, func_get_args() немного усложняет ситуацию. Например, я хочу создать метод, который действует как конструктор для определенного класса (например, фабричный метод). Мне нужно было передать параметры, переданные моему фабричному методу, конструктору класса, который я создаю.
Если вы это сделаете:
public function myFactoryMethod()
{
$class = 'SomeClass'; // e.g. you'd get this from a switch statement
$obj = new $class( func_get_args() );
return $obj;
}
а затем позвоните:
$factory->myFactoryMethod('foo','bar');
Фактически вы передаете массив в качестве первого / единственного параметра, который совпадает с new SomeClass( array( 'foo', 'bar' ) ). Очевидно, это не то, что мы хотим.
Решение (как отмечает @Seldaek) требует, чтобы мы преобразовали массив в параметры конструктора:
public function myFactoryMethod()
{
$class = 'SomeClass'; // e.g. you'd get this from a switch statement
$ref = new ReflectionClass( $class );
$obj = $ref->newInstanceArgs( func_get_args() );
return $obj;
}
Примечание. Этого нельзя было достичь с помощью call_user_func_array, потому что вы не можете использовать этот подход для создания экземпляров новых объектов.
HTH!
eval ('$ result ='. $ class Name. ':: some Static Method ()'); var_dump ($ результат);