PHP isset($this) и использование одного и того же метода объекта в статическом и объектном контексте

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

Упрощенный пример:

class Configurable{

    protected $configurations = array();

    protected static $static_configurations = array();

    public function configure($name, $value){

        // ...lots of validation logic...

        $this->configurations[$name] = $value;

        }

     public static function static_configure($name, $value){

        // ...lots of validation logic (repeated)...

        self::$static_configurations[$name] = $value;

        }

    }

Я нашел решение этой проблемы, но оно кажется очень грязным:

class Configurable{

    protected $configurations = array();

    protected static $static_configurations = array();

    public function configure($name, $value){

        // ...lots of validation logic...

        if (isset($this)){
            $this->configurations[$name] = $value;
            }
        else{
            self::$static_configurations[$name] = $value;
            }

        }

    }

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

Есть ли проблемы с областью тестирования, подобной этой? Проблемы с производительностью, проблемы с прямой совместимостью и т. д. У меня все это работает на PHP 5.2, и мне не нужно поддерживать ‹5.


person Rowan    schedule 27.02.2010    source источник


Ответы (4)


Проблема со вторым методом заключается в том, что он приведет к ошибке, если для отчета об ошибках установлено значение E_STRICT. Например:

Строгие стандарты: нестатический метод Foo::bar() не должен вызываться статически в /home/yacoby/dev/php/test.php в строке 10.

Особенность PHP6 в том, что ошибки E_STRICT перемещаются в E_ALL. Другими словами, E_ALL покроет все ошибки, включая запрет на статический вызов нестатических методов.

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

person Yacoby    schedule 27.02.2010
comment
Хорошо, кроме строгой ошибки, есть ли какие-либо проблемы со вторым методом? Я, вероятно, все равно изменю его по вашему совету, но я хотел бы понять настоящие проблемы с использованием такого кода. Это функция, которая, вероятно, будет удалена из будущих версий PHP, или вы всегда сможете вызывать нестатические методы статически? - person Rowan; 27.02.2010
comment
Ошибки E_STRICT, как правило, это кажется очень плохой идеей, но мы все равно позволим вам попробовать это сделать. Нет никаких гарантий, что такая вещь всегда будет работать, но они, вероятно, будут преобразованы в E_NOTICE или E_WARN, прежде чем полностью устареть. - person MightyE; 27.02.2010
comment
Спасибо, что разъяснили, что @MightyE - person Rowan; 27.02.2010
comment
@Yacoby Как вы можете выполнить проверку в статической fn? Это должно быть без использования isset($this), потому что $this никогда не доступно из статического fn — так как еще вы могли бы это сделать? - person ryanve; 04.05.2012

Статические методы потребуют другого количества аргументов, чем их объективный аналог — дополнительным аргументом будет контекст выполнения. Если контекста выполнения нет, то только имеет смысл вызывать его статически.

Мой предпочтительный подход, учитывая, что я создаю библиотеку с несколькими интерфейсами, подобными этому, заключается в создании статического класса и динамического класса. У одного прокси звонки на другой. Например:

class DynamicClass {
    protected $foo;
    protected $bar;
    public function baz($arg1) {
        return StaticClass::bar($this->foo, $arg1);
    }
    public function zop($arg1, $arg2) {
        return StaticClass::zop($this->foo, $this->bar, $arg1, $arg2);
    }
    // Context-less helper function
    public function womp($arg1) {
        return StaticClass::womp($arg1);
    }
}

class StaticClass {
    public static function baz(&$fooContext, $arg1) { ... }
    public static function zop(&$fooContext, &$barContext, $arg1, $arg2) { ... }
    public static function womp($arg1) { ... }
}

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


Хотя на самом деле похоже, что вы можете извлечь выгоду из шаблона проектирования Singleton. Насколько я вижу, вы пытаетесь создать глобальный Configurable, а также имеете возможность создавать отдельные локальные Configurable. С помощью одноэлементного шаблона проектирования вы создаете глобально доступную версию класса, из которой вы можете гарантировать, что у вас есть только одна из них (без нарушения принципов проектирования ООП и необходимости полагаться на $_GLOBALS и т. д.). Например:

class DynamicClass {
    protected $foo;
    protected $bar;

    public function baz($arg1) { ... }
    public function zop($arg1, $arg2) { ... }

    public static function getSingleton() {
        static $instance = null;
        if ($instance === null) $instance = new DynamicClass();
        return $instance;
    }
}

Независимо от того, где вы находитесь в своем коде, вы можете получить доступ к одному и тому же экземпляру с помощью DynamicClass::getSingleton(). У вас также есть возможность создавать одноразовые несинглтоновые версии. По сути, вы получаете лучшее из обоих миров, при этом вам нужно писать все свои методы исключительно с учетом динамического доступа.

person MightyE    schedule 27.02.2010

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

   TestRecord::generateForm(); // Generate an empty form.

   $test = new TestRecord( $primaryKey );
   [...]
   $test->generateForm();      // Generate an edit form with actual $test values.

Статическая сторона моего класса имеет дело с пустой/новой логикой, а сторона экземпляра означает, что используются живые данные.

PHP 5.3 позволяет добиться этого с помощью __call, __callStatic и static:::

public function __call( $name, $args )
{
  if ( $name == 'generateForm' ) {
    $this->fields = static::createFields();  // Action 1 : static.
    $this->fillFields();                     // Action 2 : instance.
    static::renderForm( $this->fields );     // Action 3 : static.
  }
}

public static function __callStatic( $name, $args )
{
  if ( $name == 'generateForm' ) {
    $fields = static::createFields();        // Action 1 : static.
                                             // Action 2 : none.
    static::renderForm( $fields );           // Action 3 : static.
  }
}

Примечание: квалификатор позднего связывания static:: используется, потому что мои 3 метода действий (createFields, fillFields и rendreForm) реализованы как protected в подклассах этого, то есть abstract. Это возможно, потому что PHP обеспечивает доступ к защищенным членам в обоих направлениях: от базового к подклассу, а также от подкласса к суперклассу. Насколько я знаю, это отличается от других ОО-языков.

person PFSB    schedule 04.11.2011

так как в core php мы используем index.php?var=, то чтобы сделать то же самое в oop php, что мы должны использовать.

person Sonu Bamniya    schedule 11.12.2015