PHP isset ($ this) и использует один и тот же метод объекта в статическом и объектном контексте - PullRequest
5 голосов
/ 27 февраля 2010

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

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

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. </p>

Ответы [ 4 ]

2 голосов
/ 27 февраля 2010

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

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

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 s. С помощью шаблона проектирования Singleton вы создаете глобально доступную версию класса, которую вы можете гарантировать, что у вас есть только один из них (без нарушения принципов проектирования ООП и необходимости полагаться на $ _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(). У вас также есть возможность создания одноразовых не синглтонных версий. По сути, вы получаете лучшее из обоих миров, при этом вам нужно только написать все ваши методы исключительно с учетом динамического доступа.

2 голосов
/ 04 ноября 2011

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

   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 позволяет доступ к защищенным членам в обоих направлениях: от базового до подкласса, но также и от подкласса до суперкласса. Насколько я знаю, это отличается от других ОО-языков.

2 голосов
/ 27 февраля 2010

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

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

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

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

0 голосов
/ 11 декабря 2015

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

...