Getter和Setter?

Mar*_*ark 197 php oop coding-style

我不是一个PHP开发人员,所以我不知道如果PHP是比较流行的使用显式的getter/setter方法,在纯OOP的风格,与私人领域(我喜欢的方式):

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

或只是公共领域:

class MyClass {
    public $firstField;
    public $secondField;
}
Run Code Online (Sandbox Code Playgroud)

谢谢

Dav*_*ave 211

你可以使用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;
  }
}
?>
Run Code Online (Sandbox Code Playgroud)

  • 请不要:使用魔术方法,您将放弃几乎所有与质量相关的功能,在许多IDE(甚至是vim)中:自动完成,显式PHP继承,快速PHP解释和有用的PHPDoc生成和输出.比照 http://stackoverflow.com/a/6184893/490589 (74认同)
  • 如果没有验证/卫生,那么对"公共"财产的好处是什么? (24认同)
  • 我认为你的意思是`__get`和`__set`.有两个下划线,而不是一个.这是指向页面右侧部分的直接链接:http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members(+1表示正确答案) (13认同)
  • 那不是真正的定型和吸气.通常我需要每个属性不同的getter实现! (10认同)
  • @KingCrunch,这只是一个例子.强大资源的一个非常非常简单的例子. (7认同)

Wil*_*iam 111

为什么要使用getter和setter?

  1. 可伸缩性:比搜索项目代码中的所有var赋值更容易重构getter.
  2. 调试:您可以将断点放在setter和getter上.
  3. Cleaner:Magic函数不是写入较少的好解决方案,IDE不会建议代码.更好地使用模板来快速写入getter.

直接分配和getter/setters

  • 如果您使用@property,您的IDE将建议代码(使用PhpStorm 7测试) (5认同)

mag*_*nes 38

Google已经发布了PHP优化指南,结论如下:

没有getter和setter 优化PHP

不,你不能使用魔法.对于PHP,Magic Method是邪恶的.为什么?

  1. 它们很难调试.
  2. 会对性能产生负面影响.
  3. 他们需要编写更多代码.

PHP不是Java,C++或C#.PHP是不同的,并扮演不同的角色.

  • 可以肯定的是,Google撰写的标题为"PHP性能提示"的文章并不是为了提出良好的编码风格,而是快速执行代码.处理setter和getter的章节标记为"避免编写天真的setter和getter",代码示例正是如此:Naive.它设置并获得一个没有任何验证的变量.这种二传手/吸气器毫无用处.在setter中进行验证(只需对方法参数使用类型提示)将使setter/getters变得有用,因为现在你的代码知道它正在处理什么. (12认同)
  • 我倾向于同意这个想法; `$ dog-> name ='fido'`比`$ dog-> setName('fido')更好.当实际改变一个属性时(例如:`$ dog-> increaseAge(1)`我可以构建进行必要验证的方法并改变该属性.但并非所有的动作都需要在这个意义上进行变异. (9认同)
  • 文章*没有*说"**不要**",它说"天真的制定者和吸气者". (9认同)
  • 这就像说内联样式更好.当然性能更好,但它是更好的代码吗?我还是不知道google工程师反正使用php (3认同)

net*_*der 13

封装在任何OO语言中都很重要,流行度与它无关.在动态类型语言(如PHP)中,它特别有用,因为在没有使用setter的情况下,确保属性属于特定类型的方法很少.

在PHP中,这有效:

class Foo {
   public $bar; // should be an integer
}
$foo = new Foo;
$foo->bar = "string";
Run Code Online (Sandbox Code Playgroud)

在Java中,它没有:

class Foo {
   public int bar;
}
Foo myFoo = new Foo();
myFoo.bar = "string"; // error
Run Code Online (Sandbox Code Playgroud)

使用魔术方法(__get__set)也有效,但仅在访问可见性低于当前范围的属性时才能访问.如果使用不当,在尝试调试时很容易让您头疼.

  • @smentek:你显然至少缺少了封装的一半. (14认同)
  • Getters和setter不会带来封装.封装==对象用自己的数据做某事而不是把它们放在外面.Getter和setter不是用于在PHP等动态类型语言中强制执行类型的工具. (6认同)
  • 作为对任何人的更新,PHP 7.4 将提供类型化属性支持。所以你可以在第一个例子中将 `$bar` 声明为 `int`:https://wiki.php.net/rfc/typed_properties_v2 (2认同)

J-R*_*Rou 7

如果您想使用__call函数,则可以使用此方法.它适用于

  • GET => $this->property()
  • SET => $this->property($value)
  • GET => $this->getProperty()
  • SET => $this->setProperty($value)

kalsdas

public function __call($name, $arguments) {

    //Getting and setting with $this->property($optional);

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


        //Always set the value if a parameter is passed
        if (count($arguments) == 1) {
            /* set */
            $this->$name = $arguments[0];
        } else if (count($arguments) > 1) {
            throw new \Exception("Setter for $name only accepts one parameter.");
        }

        //Always return the value (Even on the set)
        return $this->$name;
    }

    //If it doesn't chech if its a normal old type setter ot getter
    //Getting and setting with $this->getProperty($optional);
    //Getting and setting with $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':
            //Always set the value if a parameter is passed
            if (count($arguments) != 1) {
                throw new \Exception("Setter for $name requires exactly one parameter.");
            }
            $this->$property = $arguments[0];
            //Always return the value (Even on the set)
            return $this->$name;
        default:
            throw new \Exception("Property $name doesn't exist.");
            break;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @ krzysztof-przygoda:这个"魔术方法"总是有代价的.他们必须使用递归`property_exists(get_class($ this),$ name)`并且递归很慢.有一种方法可以通过缓存来缓解这种情况,但它仍然比手动创建getter和setter要慢.我只是写了这个作为替代.我实际上不建议使用"魔术方法".创建getter和setter的额外时间通常是微不足道的. (2认同)

Lor*_*uer 7

除了这里已经很好的和受尊重的答案之外,我想扩展PHP没有setter/getters.

PHP没有getter和setter语法.它提供了子类或魔术方法,允许"挂钩"并覆盖属性查找过程,如 Dave所指出的那样.

Magic允许我们懒惰的程序员在我们积极参与项目并且密切了解它的时候用更少的代码做更多的事情,但通常以牺牲可读性为代价.

性能由于在PHP中强制使用类似getter/setter的代码体系结构而导致的每个不必要的函数都会在调用时涉及自己的内存堆栈帧并浪费CPU周期.

可读性:代码库会产生膨胀的代码行,这会影响代码导航,因为更多的LOC意味着更多的滚动.

偏好:就个人而言,作为我的经验法则,我将静态代码分析的失败作为一个标志,以避免走在神奇的道路上,只要当时没有明显的长期利益.

误区:

一个常见的论点是可读性.例如,$someobject->width比阅读更容易阅读$someobject->width().然而,与行星的circumference或者width可以假设的不同 static,对象的实例(例如$someobject,需要宽度函数)可能需要测量对象的实例宽度.
因此,可读性的增加主要是因为断言命名方案,而不是通过隐藏输出给定属性值的函数.

__get/__set使用:

  • 财产价值的预验证和预先卫生

  • 字符串例如

    "
    some {mathsobj1->generatelatex} multi
    line text {mathsobj1->latexoutput}
    with lots of variables for {mathsobj1->generatelatex}
     some reason
    "
    
    Run Code Online (Sandbox Code Playgroud)

    在这种情况下,generatelatex将遵循actionname + methodname的命名方案

  • 特殊,明显的案例

    $dnastringobj->homeobox($one_rememberable_parameter)->gattaca->findrelated()
    $dnastringobj->homeobox($one_rememberable_parameter)->gttccaatttga->findrelated()
    
    Run Code Online (Sandbox Code Playgroud)

注意: PHP选择不实现getter/setter语法.我并不是说getter/setter一般都不好.


小智 6

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( 'Can\'t get property ' . $name );
    }

    public function __set( $name , $value ) {
        if( method_exists( $this , $method = ( 'set' . ucfirst( $name  ) ) ) )
            return $this->$method( $value );
        else
            throw new Exception( 'Can\'t set property ' . $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; // works
$obj->firstField = 'value'; // works

echo $obj->getFirstField(); // works
$obj->setFirstField( 'value' ); // not works, method is protected

echo $obj->secondField; // works
echo $obj->getSecondField(); // not works, method is private

$obj->secondField = 'value'; // not works, setter not exists

echo $obj->thirdField; // not works, property not exists

isset( $obj->firstField ); // returns true
isset( $obj->secondField ); // returns true
isset( $obj->thirdField ); // returns false
Run Code Online (Sandbox Code Playgroud)

准备!


Sab*_*baz 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];
    }
}
Run Code Online (Sandbox Code Playgroud)

只需在您的类中添加上面的方法,现在您可以输入:

class MyClass{
    private foo = "bar";
    private bom = "bim";
    // ...
    // public function __call(){ ... }
    // ...
}
$C = new MyClass();

// as getter
$C->get_foo(); // return "bar"
$C->get_bom(); // return "bim"

// as setter
$C->set_foo("abc"); // set "abc" as new value of foo
$C->set_bom("zam"); // set "zam" as new value of bom
Run Code Online (Sandbox Code Playgroud)

这样,您可以获取/设置类中的所有内容(如果存在),因此,如果您仅需要少数特定元素,则可以使用“白名单”作为过滤器。

例子:

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];
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您只能获取/设置“foo”和“fee”。
您还可以使用该“白名单”来分配自定义名称以访问您的变量。
例如,

private $callWhiteList = array(
    "myfoo" => "foo",
    "zim" => "bom",
    // ...
);
Run Code Online (Sandbox Code Playgroud)

通过该列表,您现在可以输入:

class MyClass{
    private foo = "bar";
    private bom = "bim";
    // ...
    // private $callWhiteList = array( ... )
    // public function __call(){ ... }
    // ...
}
$C = new MyClass();

// as getter
$C->get_myfoo(); // return "bar"
$C->get_zim(); // return "bim"

// as setter
$C->set_myfoo("abc"); // set "abc" as new value of foo
$C->set_zim("zam"); // set "zam" as new value of bom
Run Code Online (Sandbox Code Playgroud)




就这样。


文档: 在对象上下文中调用不可访问的方法时会触发__call() 。


Ema*_*olm 5

那么,PHP确实有神奇的方法__get,__set,__isset__unset,这始终是一个开始.唉正确(得到它?)OO属性不仅仅是魔术方法.PHP实现的主要问题是为所有不可访问的属性调用魔术方法.这意味着在确定name是否实际上是对象的属性时,您必须在魔术方法中重复自己(例如,通过调用property_exists()).除非你的所有类继承自ie,否则你无法用基类真正解决这个一般性问题.ClassWithProperties,因为PHP缺少多重继承.

相比之下,Python新样式类为您提供了property(),它允许您显式定义所有属性.C#有特殊的语法.

http://en.wikipedia.org/wiki/Property_(programming)