Последствия создания экземпляров объектов с динамическими переменными в PHP

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

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

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

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

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

Какие еще последствия существуют или какие термины поисковых систем, кроме "Rank PHP Hackery", можно использовать для исследования?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
12
0
5 966
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

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

<?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
?>

eval ('$ result ='. $ class Name. ':: some Static Method ()'); var_dump ($ результат);

Michał Niedźwiedzki 15.09.2008 16:39

call_user_func (массив ($ className, 'someStaticMethod'));

bd808 18.07.2012 08:07

@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!

Другие вопросы по теме