Последствия создания объектов с динамическими переменными в PHP - PullRequest
11 голосов
/ 07 сентября 2008

Каковы последствия для производительности, безопасности или «других» использования следующей формы для объявления нового экземпляра класса в PHP

<?php
  $class_name = 'SomeClassName';
  $object = new $class_name;
?>

Это надуманный пример, но я видел, как эта форма используется на фабриках (ООП), чтобы избежать большого оператора if / switch.

Проблемы, которые сразу приходят на ум,

  1. Вы теряете способность передавать аргументы в конструктор (ЛОЖЬ. Спасибо Джереми)
  2. Пахнет как eval (), со всеми проблемами безопасности, которые он приносит на стол (но не обязательно с проблемами производительности?)

Какие есть другие последствия, или какие термины поисковой системы, кроме "Rank PHP Hackery", могут кто-то использовать для исследования этого?

Ответы [ 10 ]

8 голосов
/ 08 сентября 2008

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

Пока вы не делаете что-то вроде

$classname = 'SomeClassName';
for ($x = 0; $x < 100000; $x++){
  $object = new $classname;
}

Вы, вероятно, в порядке: -)

(моя точка зрения такова: динамический поиск класса здесь, а затем не повредит. Если вы будете делать это часто, это будет).

Кроме того, убедитесь, что $ classname никогда не может быть установлен извне - вы хотите иметь некоторый контроль над тем, какой именно класс вы будете создавать.

8 голосов
/ 07 сентября 2008

Похоже, вы все еще можете передавать аргументы в конструктор, вот мой тестовый код:

<?php

class Test {
    function __construct($x) {
        echo $x;
    }
}

$class = 'Test';
$object = new $class('test'); // echoes "test"

?>

Это то, что вы имели в виду, верно?

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

5 голосов
/ 15 сентября 2008

Я бы добавил, что вы также можете создать его с динамическим числом параметров, используя:

<?php

$class = "Test";
$args = array('a', 'b');
$ref = new ReflectionClass($class);
$instance = $ref->newInstanceArgs($args);

?>

Но, конечно, вы добавляете дополнительные накладные расходы, делая это.

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

4 голосов
/ 07 сентября 2008

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

Мы используем это соглашение повсеместно в некоторых базовых классах нашей внутренней инфраструктуры, чтобы заставить URL-адреса работать с отображениями контроллера. Я также видел это во многих коммерческих приложениях с открытым исходным кодом (я постараюсь найти пример и опубликовать его). В любом случае, смысл моего ответа заключается в том, что, по-видимому, стоит немного снизить производительность, если он делает более гибкий и динамический код.

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

2 голосов
/ 08 сентября 2008

Алан, нет ничего плохого в динамической инициализации класса. Эта техника присутствует и в языке Java, где можно преобразовать строку в класс, используя метод Class.forClass('classname'). Также очень удобно переносить сложность алгоритма на несколько классов вместо того, чтобы иметь список условий if. Динамические имена классов особенно хорошо подходят для ситуаций, когда вы хотите, чтобы ваш код оставался открытым для расширения без необходимости изменений.

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

Вы не должны беспокоиться о производительности. У него почти нет накладных расходов, а сами объекты в PHP очень быстрые. Если вам нужно создать тысячи одинаковых объектов, используйте шаблон проектирования Flyweight , чтобы уменьшить объем памяти. В частности, вы не должны жертвовать временем как разработчиком ради экономии миллисекунд на сервере. Кроме того, оптимизаторы кода операции работают без проблем с этой техникой. Скрипты, скомпилированные с помощью Zend Optimizer, не работают неправильно.

1 голос
/ 08 января 2013

Итак, я недавно столкнулся с этим и хотел высказать свои мысли о «других» последствиях использования динамической реализации.

С одной стороны, 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;
}

, а затем позвоните:

$ заводской> myFactoryMethod ( 'Foo', 'бар');

Вы фактически передаете массив как первый / единственный параметр, который совпадает с 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, поскольку вы не можете использовать этот подход для создания новых объектов.

НТН!

0 голосов
/ 20 января 2009
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 наслаждайтесь ..

0 голосов
/ 15 сентября 2008

@ coldFlame: IIRC вы можете использовать call_user_func(array($className, 'someStaticMethod') и call_user_func_array() для передачи параметров

0 голосов
/ 15 сентября 2008

Одна проблема в том, что вы не можете обращаться к таким статическим элементам, например,

<?php
$className = 'ClassName';

$className::someStaticMethod(); //doesn't work
?>
0 голосов
/ 08 сентября 2008

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

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

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