Web

Стоит ли использовать геттеры/сеттеры в PHP

Я не PHP-разработчик, поэтому мне интересно, является ли в PHP более популярным использование явных getter/setters в чистом стиле ООП для приватных полей класса:

class MyClass {

    private $firstField;

    private $secondField;

 

    public function getFirstField() {

        return $this->firstField;

    }

    public function setFirstField($x) {

        $this->firstField = $x;

    }

    public function getSecondField() {

        return $this->secondField;

    }

    public function setSecondField($x) {

        $this->secondField = $x;

    }

}

 или для публичных полей:

class MyClass {

    public $firstField;

    public $secondField;

}

 

 Ответ 1

Вы можете использовать «магические» методы php __get и __set:

<?php

class MyClass {

  private $firstField;

  private $secondField;

 

  public function __get($property) {

    if (property_exists($this, $property)) {

      return $this->$property;

    }

  }

 

  public function __set($property, $value) {

    if (property_exists($this, $property)) {

      $this->$property = $value;

    }

    return $this;

  }

}

?>

 

Ответ 2

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

В PHP это работает так:

class Foo {

   public $bar; // должно быть целым числом

}

$foo = new Foo;

$foo->bar = "string";

 В Java — по-другому:

class Foo {

   public int bar;

}

Foo myFoo = new Foo();

myFoo.bar = "string"; // ошибка

Использование «магических» методов (__get и __set) также работает, но только при доступе к свойству, которое имеет меньшую видимость, чем может получить текущая область видимости. Это может вызвать проблемы при попытке отладки, если использовать его неправильно.

 

Ответ 3

В дополнение к предыдущим ответам я хотел бы расширить информацию о том, что в PHP нет сеттеров/геттеров. В PHP нет синтаксиса getter и setter. Он предоставляет подклассы или «магические» методы, позволяющие «подцепить» и переопределить процесс поиска свойств.

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

 Производительность: Каждая ненужная функция, возникающая в результате принудительного использования в PHP архитектуры кода, подобной getter/setter, при вызове занимает свой собственный стек памяти и тратит процессорные циклы.

Читабельность: В кодовой базе появляются раздутые строки кода, что влияет на навигацию по коду, так как большее количество LOC означает большее количество прокруток.

Предпочтения: Лично я воспринимаю неудачу статического анализа кода как знак того, что не стоит идти по неизвестному пути, пока очевидные долгосрочные преимущества ускользают от меня в определенный момент.

Восприятие: Распространенным аргументом является удобочитаемость. Например, что $someobject->width легче прочитать, чем $someobject->width(). Однако в отличие от конкретных данных, которые можно считать статичными, экземпляр объекта, такой как $someobject, которому требуется функция width, скорее всего, измеряет ширину экземпляра объекта.

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

Использование __get / __set:

Предварительная валидация и предварительная санация значений свойств.

Например, для строк:

"

some {mathsobj1->generatelatex} multi

line text {mathsobj1->latexoutput}

with lots of variables for {mathsobj1->generatelatex}

 some reason

"

В этом случае generatelatex будет придерживаться схемы именования actionname + methodname.

Или для специальных, очевидных случаев:

$dnastringobj->homeobox($one_rememberable_parameter)->gattaca->findrelated()

$dnastringobj->homeobox($one_rememberable_parameter)->gttccaatttga->findrelated()

 

Примечание: в PHP решили не реализовывать синтаксис getter/setter. Хотя я не утверждаю, что геттеры/сеттеры в целом плохи.

 

Ответ 4

class MyClass {

    private $firstField;

    private $secondField;

    private $thirdField;

 

    public function __get( $name ) {

        if( method_exists( $this , $method = ( 'get' . ucfirst( $name  ) ) ) )

            return $this->$method();

        else

            throw new Exception( 'Свойство отсутствует ' . $name );

    }

 

    public function __set( $name , $value ) {

        if( method_exists( $this , $method = ( 'set' . ucfirst( $name  ) ) ) )

            return $this->$method( $value );

        else

            throw new Exception( 'Свойство отсутствует' . $name );

    }

 

    public function __isset( $name ) {

        return method_exists( $this , 'get' . ucfirst( $name  ) ) 

            || method_exists( $this , 'set' . ucfirst( $name  ) );

    }

 

    public function getFirstField() {

        return $this->firstField;

    }

 

    protected function setFirstField($x) {

        $this->firstField = $x;

    }

 

    private function getSecondField() {

        return $this->secondField;

    }

}

 

$obj = new MyClass();

echo $obj->firstField; // работает

$obj->firstField = 'value'; // работает

echo $obj->getFirstField(); // работает

$obj->setFirstField( 'value' ); // не работает, защищенный метод

echo $obj->secondField; // работает

echo $obj->getSecondField(); // не работает, приватный метод

$obj->secondField = 'value'; // не работает, сеттер не существует

echo $obj->thirdField; // не работает, свойство не существует

isset( $obj->firstField ); // возвращает true

isset( $obj->secondField ); // возвращает true

isset( $obj->thirdField ); // возвращает false

 

Ответ 5

Если вы предпочитаете использовать функцию __call, вы можете воспользоваться этим методом. Он работает с:

  1. GET => $this->property()

  2. SET => $this->property($value)

  3. GET => $this->getProperty()

  4. SET => $this->setProperty($value)

  public function __call($name, $arguments) {

    //Получение и установка с помощью $this->property($optional);

    if (property_exists(get_class($this), $name)) {

        //Всегда устанавливайте значение, если передан параметр

        if (count($arguments) == 1) {

            /* установка */

            $this->$name = $arguments[0];

        } else if (count($arguments) > 1) {

            throw new \Exception("Сеттер для $name принимает только один параметр.");

        }

        //Всегда возвращает значение (даже на множестве)

        return $this->$name;

    }

    //Если это не имеет значения, это обычный сеттер старого типа или геттер.

    //Получение и установка с помощью $this->getProperty($optional);

    //Получение и установка с помощью $this->setProperty($optional);

    $prefix = substr($name, 0, 3);

    $property = strtolower($name[3]) . substr($name, 4);

    switch ($prefix) {

        case 'get':

            return $this->$property;

            break;

        case 'set':

            //Всегда устанавливайте значение, если передан параметр

            if (count($arguments) != 1) {

                throw new \Exception("Сеттер для $name принимает только один параметр.");

            }

            $this->$property = $arguments[0];

            //Всегда возвращает значение (даже на множестве)

            return $this->$name;

        default:

            throw new \Exception("Свойство $name не существует.");

            break;

    }

}

 

Ответ 6

Я провел эксперимент, используя «магический» метод __call. Не уверен, стоит ли его публиковать (из-за всех предупреждений «НЕ ИСПОЛЬЗУЙТЕ МАГИЧЕСКИЕ МЕТОДЫ» в других ответах), но я оставлю его здесь... на случай, если кому-то он окажется полезным.

public function __call($_name, $_arguments){

    $action  = substr($_name, 0, 4);

    $varName = substr($_name, 4);

    if (isset($this->{$varName})){

        if ($action === "get_") return $this->{$varName};

        if ($action === "set_") $this->{$varName} = $_arguments[0];

    }

}

Просто добавьте этот метод выше в свой класс, теперь вы можете использовать его так:

class MyClass{

    private foo = "bar";

    private bom = "bim";

    // ...

    // public function __call(){ ... }

    // ...

}

$C = new MyClass();

 

// как геттер

$C->get_foo(); // вернет "bar"

$C->get_bom(); // вернет "bim"

 

// как сеттер

$C->set_foo("abc"); // установит "abc"  в новое значение foo

$C->set_bom("zam"); // установит "zam"  в новое значение bom

 Таким образом, вы можете получить/установить все в вашем классе, если он существует, поэтому, если вам это нужно только для нескольких определенных элементов, вы можете использовать «белый список» в качестве фильтра. Пример:

private $callWhiteList = array(

    "foo" => "foo",

    "fee" => "fee",

    // ...

);

 

public function __call($_name, $_arguments){

    $action  = substr($_name, 0, 4);

    $varName = $this->callWhiteList[substr($_name, 4)];

    if (!is_null($varName) && isset($this->{$varName})){

        if ($action === "get_") return $this->{$varName};

        if ($action === "set_") $this->{$varName} = $_arguments[0];

    }

}

 Теперь вы можете получить/установить только «foo» и «fee». Вы также можете использовать этот «белый список» для назначения пользовательских имен для доступа к вашим параметрам. Например:

private $callWhiteList = array(

    "myfoo" => "foo",

    "zim" => "bom",

    // ...

);

 

С этим списком вы теперь можете создать код:

class MyClass{

    private foo = "bar";

    private bom = "bim";

    // ...

    // private $callWhiteList = array( ... )

    // public function __call(){ ... }

    // ...

}

$C = new MyClass();

// как геттер

$C->get_myfoo(); // вернет "bar"

$C->get_zim(); // вернет "bim"

 

// как сеттер

$C->set_myfoo("abc"); // устанавливает "abc" в новое значение foo

$C->set_zim("zam"); // устанавливает "zam" в новое значение bom

 

 Ответ 7

Прочитав другие ответы, я пришел к следующим выводам:

Вы не всегда будете определять сеттеры для ВСЕХ свойств, особенно «внутренних» (семафоры, внутренние флаги...). Свойства только для чтения, очевидно, не будут иметь сеттеров, поэтому некоторые свойства будут иметь только геттеры; вот где __get() приходит на помощь, чтобы сократить код:

  1. Определите __get() (магические глобальные геттеры) для всех одинаковых свойств.

  2. Сгруппируйте их в массивы так:

1. чтобы они имели общие характеристики: финансовые значения будут/могут быть правильно отформатированы, даты в определенном формате (ISO, US, Intl.) и т. д.;

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

3. если вам нужно создать новое подобное свойство, просто объявите его и добавьте его имя в соответствующий массив, и все готово. Это намного быстрее, чем определять новый getter, возможно, с несколькими строками кода, повторяющимися снова и снова по всему коду класса.

  1. Мы могли бы написать приватный метод для этого, но тогда у нас будет МНОГО объявленных методов, которые в итоге вызывают другой, всегда один и тот же метод. Почему бы просто не написать ОДИН метод, который бы управлял ими всеми.

  2. Сеттеры также могут реагировать ТОЛЬКО на определенные свойства, поэтому все свойства типа даты могут быть проверены на недопустимые значения лишь в одном методе. Если свойства типа даты перечислены в массиве, их сеттеры могут быть легко определены. Конечно, это только пример. Ситуаций может быть слишком много.

Языки программирования это всего лишь языки, созданные человеком. Каждый из них имеет свою интонацию или акцент, синтаксис и стиль, поэтому я не буду притворяться, что пишу код на Ruby или Python с тем же «акцентом», что и на Java или C#, и не буду писать JavaScript или PHP так, чтобы они походили на Perl или SQL... Используйте их так, как они предназначены для использования.

Схожие статьи

Web

Получение атрибута href элемента A

Web

Как применить метод bindValue в предложении SQL LIMIT?

Web

Как получить куки из php curl в переменную

Workplace Facebook: что это за платформа и как ей пользоваться?
Web

Workplace Facebook: что это за платформа и как ей пользоваться?