在 PHP 中使用 getter 和 setter 代替函数或简单的公共字段有什么优点?

发布于 2024-10-08 17:34:45 字数 1702 浏览 5 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

放血 2024-10-15 17:34:45

您可以使用 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;
  }
}
?>

You can use php magic methods __get and __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;
  }
}
?>
瑕疵 2024-10-15 17:34:45

为什么要使用 getter 和 setter?

  1. 可扩展性:重构 getter 比搜索项目代码中的所有 var 分配更容易。
  2. 调试:您可以在 setter 和 getter 处放置断点。
  3. 更干净:魔术函数并不是减少编写的好解决方案,您的 IDE 不会建议代码。更好地使用模板来快速编写 getter。

直接赋值和 getters/setters

Why use getters and setters?

  1. Scalability: It's easier refactor a getter than search all the var assignments in a project code.
  2. Debugging: You can put breakpoints at setters and getters.
  3. Cleaner: Magic functions are not good solution for writting less, your IDE will not suggest the code. Better use templates for fast-writting getters.

direct assignment and getters/setters

两人的回忆 2024-10-15 17:34:45

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

没有 getter 和 setter 优化 PHP

不,您不能使用魔法方法。对于 PHP 来说,魔法方法是邪恶的。为什么?

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

PHP 不是 Java、C++ 或 C#。 PHP 是不同的,并且遵循不同的规则。

Google already published a guide on optimization of PHP and the conclusion was:

No getter and setter Optimizing PHP

And no, you must not use magic methods. For PHP, Magic Methods are evil. Why?

  1. They are hard to debug.
  2. There is a negative performance impact.
  3. They require writing more code.

PHP is not Java, C++, or C#. PHP is different and plays with different rules.

眼眸印温柔 2024-10-15 17:34:45

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

在 PHP 中,这是有效的:

class Foo {
   public $bar; // should be an integer
}
$foo = new Foo;
$foo->bar = "string";

在 Java 中,则不然:

class Foo {
   public int bar;
}
Foo myFoo = new Foo();
myFoo.bar = "string"; // error

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

Encapsulation is important in any OO language, popularity has nothing to do with it. In dynamically typed languages, like PHP, it is especially useful because there is little ways to ensure a property is of a specific type without using setters.

In PHP, this works:

class Foo {
   public $bar; // should be an integer
}
$foo = new Foo;
$foo->bar = "string";

In Java, it doesn't:

class Foo {
   public int bar;
}
Foo myFoo = new Foo();
myFoo.bar = "string"; // error

Using magic methods (__get and __set) also works, but only when accessing a property that has lower visibility than the current scope can access. It can easily give you headaches when trying to debug, if it is not used properly.

嘿哥们儿 2024-10-15 17:34:45

如果您喜欢使用 __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;
    }
}

If you preffer to use the __call function, you can use this method. It works with

  • 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;
    }
}
懵少女 2024-10-15 17:34:45

除了这里已经很棒且受人尊敬的答案之外,我还想扩展一下没有 setter/getter 的 PHP。

PHP 没有 getter 和 setter 语法。正如 Dave 所指出的,它提供了子类或神奇方法来允许“挂钩”并覆盖属性查找过程。

Magic 允许我们这些懒惰的程序员做更多事情当我们积极参与一个项目并熟悉它时,代码会减少,但通常会牺牲可读性。

性能 每个不必要的函数(由于在 PHP 中强制使用类似 getter/setter 的代码架构而产生)在调用时都会涉及到自己的内存堆栈帧,并且会浪费 CPU 周期。

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

偏好:就我个人而言,根据我的经验,我认为静态代码分析的失败
只要当时我无法获得明显的长期利益,就可以作为避免走上神奇之路的标志。

谬误:

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

__get / __set 使用:

  • 属性值的预验证和预清理

  • 字符串例如

    <前><代码>”
    一些 {mathsobj1->generatelatex} 多重
    行文本 {mathsobj1->latexoutput}
    {mathsobj1->generatelatex} 有很多变量
    某种原因

    在这种情况下generatelatex将遵循actionname + methodname

  • 特殊、明显的情况

    $dnastringobj->homeobox($one_rememberable_parameter)->gattaca->findlated()
    $dnastringobj->homeobox($one_rememberable_parameter)->gttccaatttga->findlinked()
    

注意: PHP 选择不实现 getter/setter 语法。我并不是说 getter/setter 通常都是不好的。

In addition to the already great and respected answers in here, I would like to expand on PHP having no setters/getters.

PHP does not have getter and setter syntax. It provides subclassed or magic methods to allow "hooking" and overriding the property lookup process, as pointed out by Dave.

Magic allows us lazy programmers to do more with less code at a time at which we are actively engaged in a project and know it intimately, but usually at the expense of readability.

Performance Every unnecessary function, that results from forcing a getter/setter-like code-architecture in PHP, involves its own memory stack-frame upon invocation and is wasting CPU cycles.

Readability: The codebase incurs bloating code-lines, which impacts code-navigation as more LOC mean more scrolling,.

Preference: Personally, as my rule of thumb, I take the failure of static code analysis
as a sign to avoid going down the magical road as long as obvious long-term benefits elude me at that time.

Fallacies:

A common argument is readability. For instance that $someobject->width is easier to read than $someobject->width(). However unlike a planet's circumference or width, which can be assumed to be static, an object's instance such as $someobject, which requires a width function, likely takes a measurement of the object's instance width.
Therefore readability increases mainly because of assertive naming-schemes and not by hiding the function away that outputs a given property-value.

__get / __set uses:

  • pre-validation and pre-sanitation of property values

  • strings e.g.

    "
    some {mathsobj1->generatelatex} multi
    line text {mathsobj1->latexoutput}
    with lots of variables for {mathsobj1->generatelatex}
     some reason
    "
    

    In this case generatelatex would adhere to a naming scheme of actionname + methodname

  • special, obvious cases

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

Note: PHP chose not to implement getter/setter syntax. I am not claiming that getters/setter are generally bad.

顾挽 2024-10-15 17:34:45
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

准备好!

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

Ready!

-柠檬树下少年和吉他 2024-10-15 17:34:45

我使用魔术方法 __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();

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


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

示例:

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();

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

.
.
.
就这样。


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

I made an experiment using the magic method __call.
Not sure if I should post it (because of all the "DO NOT USE MAGIC METHODS" warnings in the other answers and comments) but i'll leave it here.. just in case someone find it useful.


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];
    }
}

Just add that method above in your class, now you can type:

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


This way you can get/set everything in your class if it exist so, if you need it for only a few specific elements, you could use a "whitelist" as filter.

Example:

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];
    }
}

Now you can only get/set "foo" and "fee".
You can also use that "whitelist" to assign custom names to access to your vars.
For example,

private $callWhiteList = array(
    "myfoo" => "foo",
    "zim" => "bom",
    // ...
);

With that list you can now type:

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

.
.
.
That's all.


Doc:
__call() is triggered when invoking inaccessible methods in an object context.

猫七 2024-10-15 17:34:45

嗯,PHP 确实有神奇的方法 __get__set__isset & 。 __unset,这始终是一个开始。唉,正确的(明白了吗?)OO 属性不仅仅是魔法方法。 PHP 实现的主要问题是为所有 不可访问的属性调用魔术方法。这意味着在确定 name 是否实际上是对象的属性时,您必须在魔术方法中重复自己(例如,通过调用 property_exists())。除非所有类都继承自 ie,否则您无法真正使用基类解决这个普遍问题。 ClassWithProperties,因为 PHP 缺乏多重继承。

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

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

Well, PHP does have magic methods __get, __set, __isset & __unset, which is always a start. Alas proper (get it?) OO properties is more than magic methods. The main problem with PHP's implementation is that magic methods are called for all inaccessible properties. Which means you have to Repeat Yourself (eg. by calling property_exists()) in the magic methods when determining if name is actually a property of your object. And you can't really solve this general problem with a base class unless all your classes inherit from ie. ClassWithProperties, since PHP lacks multiple inheritance.

In contrast, Python new style classes gives you property(), which lets you explicitly define all your properties. C# has special syntax.

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

桃扇骨 2024-10-15 17:34:45

阅读完其他建议后,我倾向于这样说:

作为 GENERIC 规则,您不会总是为 ALL 属性定义设置器,特别是“内部”属性(信号量) ,内部标志...)。显然,只读属性不会有 setter,因此某些属性只会有 getter;这就是 __get() 缩小代码的地方:

  • 为所有相似的属性定义一个 __get() (神奇的全局 getter),
  • 将它们分组在数组中,如下所示:
    • 它们将具有共同的特征:货币值将/可能以正确的格式显示、特定布局中的日期(ISO、美国、国际)等。
    • 代码本身可以验证只有现有的 &允许的属性正在使用这种神奇的方法读取。
    • 每当您需要创建一个新的类似属性时,只需声明它并将其名称添加到正确的数组中即可完成。这比定义一个新的 getter 更快,也许在整个类代码中一遍又一遍地重复某些代码行。

是的!我们也可以编写一个私有方法来做到这一点,但话又说回来,我们将声明许多方法(++内存),最终会调用另一个始终相同的方法。为什么不编写一个SINGLE方法来统治它们...? [是的!双关语绝对是故意的! :)]

魔术设置器也可以仅响应特定属性,因此可以仅在一种方法中针对无效值筛选所有日期类型属性。如果日期类型属性在数组中列出,则可以轻松定义它们的设置器。当然,这只是一个例子。有太多的情况。

关于可读性...嗯...这是另一场争论:我不喜欢被 IDE 的使用所束缚(事实上,我不使用它们,它们倾向于告诉我(并且强迫我)如何编写......并且我喜欢编码“美”)。我倾向于在命名方面保持一致,因此使用 ctags 和其他一些辅助工具对我来说就足够了......无论如何:一旦完成所有这些神奇的 setter 和 getter,我会编写其他过于具体或“特殊”的 setter可以概括为 __set() 方法。这涵盖了我需要的有关获取和设置属性的所有内容。当然:并不总是有共同点,或者有一些属性不值得编写一个神奇的方法,然后仍然是古老的良好的传统 setter/getter 对。

编程语言就是:人类人工语言。因此,它们中的每一个都有自己的语调或重音、语法和风格,因此我不会假装使用与 Java 或 C# 相同的“重音”来编写 Ruby 或 Python 代码,也不会编写类似的 JavaScript 或 PHP Perl 或 SQL...按其应有的方式使用它们。

After reading the other advices, I'm inclined to say that:

As a GENERIC rule, you will not always define setters for ALL properties, specially "internal" ones (semaphores, internal flags...). Read-only properties will not have setters, obviously, so some properties will only have getters; that's where __get() comes to shrink the code:

  • define a __get() (magical global getters) for all those properties which are alike,
  • group them in arrays so:
    • they'll share common characteristics: monetary values will/may come up properly formatted, dates in an specific layout (ISO, US, Intl.), etc.
    • the code itself can verify that only existing & allowed properties are being read using this magical method.
    • whenever you need to create a new similar property, just declare it and add its name to the proper array and it's done. That's way FASTER than defining a new getter, perhaps with some lines of code REPEATED again and again all over the class code.

Yes! we could write a private method to do that, also, but then again, we'll have MANY methods declared (++memory) that end up calling another, always the same, method. Why just not write a SINGLE method to rule them all...? [yep! pun absolutely intended! :)]

Magic setters can also respond ONLY to specific properties, so all date type properties can be screened against invalid values in one method alone. If date type properties were listed in an array, their setters can be defined easily. Just an example, of course. there are way too many situations.

About readability... Well... That's another debate: I don't like to be bound to the uses of an IDE (in fact, I don't use them, they tend to tell me (and force me) how to write... and I have my likes about coding "beauty"). I tend to be consistent about naming, so using ctags and a couple of other aids is sufficient to me... Anyway: once all this magic setters and getters are done, I write the other setters that are too specific or "special" to be generalized in a __set() method. And that covers all I need about getting and setting properties. Of course: there's not always a common ground, or there are such a few properties that is not worth the trouble of coding a magical method, and then there's still the old good traditional setter/getter pair.

Programming languages are just that: human artificial languages. So, each of them has its own intonation or accent, syntax and flavor, so I won't pretend to write a Ruby or Python code using the same "accent" than Java or C#, nor I would write a JavaScript or PHP to resemble Perl or SQL... Use them the way they're meant to be used.

夏末的微笑 2024-10-15 17:34:45

一般来说,第一种方式总体上更受欢迎,因为那些具有编程知识的人可以轻松过渡到 PHP 并以面向对象的方式完成工作。第一种方式更为通用。我的建议是坚持在多种语言中经过尝试和正确的做法。然后,当您使用另一种语言时,您将准备好完成一些事情(而不是花时间重新发明轮子)。

Generally speaking, the first way is more popular overall because those with prior programming knowledge can easily transition to PHP and get work done in an object-oriented fashion. The first way is more universal. My advice would be to stick with what is tried and true across many languages. Then, when and if you use another language, you'll be ready to get something accomplished (instead of spending time reinventing the wheel).

诗笺 2024-10-15 17:34:45

在 netbeans 约定中创建源代码的方法有很多。这很好。它让思考变得更容易===错。只需使用传统方法,特别是当您不确定应该封装哪一项属性以及不应该封装哪一项属性时。我知道,这是一个 boi.... pla... 代码,但对于调试工作和许多其他人来说,这是更好、更清晰的方法。不要花太多时间研究如何制作简单的 getter 和 setter 的数千种艺术。如果你使用魔法,你就不能实现太多的设计模式,比如德米特规则等等。在特定情况下,您可以使用 magic_calls 或小型、快速且清晰的解决方案。当然,您也可以通过这种方式为设计模式制定解决方案,但为什么会让您的生活变得更加困难。

There are many ways to create sourcecode in a netbeans-convention. This is nice. It makes thinks such easyer === FALSE. Just use the traditionel, specially if you are not sure which one of the properties should be encapsuled and which one not. I know, it is a boi.... pla... code, but for debugging-works and many other thinks it is the better, clear way. Dont spend to much time with thousend of arts how to make simple getters and setters. You cannot implement too some design patterns like the demeter-rule and so on, if you use magics. In specific situation you can use magic_calls or for small, fast and clear solutions. Sure you could make solutions for design-patters in this way too, but why to make you live more difficult.

像你 2024-10-15 17:34:45

验证 + 格式化/导出值

Setters 让您验证数据,getters 让您格式化或导出数据。对象允许您将数据及其验证和格式化代码封装到一个鼓励 DRY 的整洁包中。

例如,考虑以下包含出生日期的简单类。

class BirthDate {

    private $birth_date;

    public function getBirthDate($format='Y-m-d') {
        //format $birth_date ...
        //$birth_date = ...
        return $birth_date;
    }

    public function setBirthDate($birth_date) {                   
        //if($birth_date is not valid) throw an exception ...          
        $this->birth_date = $birth_date;
    }

    public function getAge() {
        //calculate age ...
        return $age;
    }

    public function getDaysUntilBirthday() {
        //calculate days until birth days
        return $days;
    }
}

您需要验证所设置的值是否为

  • 有效日期,
  • 而不是将来

,并且您不想在整个应用程序(或就此而言的多个应用程序)上进行此验证。相反,将成员变量设置为受保护或私有(以便使 setter 成为唯一的访问点)并在 setter 中进行验证会更容易,因为这样您就会知道该对象包含有效的出生日期,无论该对象的哪一部分对象来自的应用程序,如果您想添加更多验证,则可以将其添加在一个位置。

您可能希望添加对同一成员变量进行操作的多个格式化程序,即 getAge()getDaysUntilBirthday(),并且您可能希望在 getBirthDate 中强制执行可配置格式() 取决于区域设置。因此,我更喜欢通过 getters 一致地访问值,而不是将 $date->getAge()$date->birth_date 混合在一起。

当您扩展对象时,getter 和 setter 也很有用。例如,假设您的应用程序需要在某些地方允许 150 年以上的出生日期,但在其他地方则不允许。在不重复任何代码的情况下解决问题的一种方法是扩展 BirthDate 对象并将附加验证放入 setter 中。

class LivingBirthDate extends BirthDate {

    public function setBirthDate($birth_date) {
        //if $birth_date is greater than 150 years throw an exception
        //else pass to parent's setter
        return parent::setBirthDate($birth_date);
    }
}

Validating + Formatting/Deriving Values

Setters let you to validate data and getters let you format or derive data. Objects allow you to encapsulate data and its validation and formatting code into a neat package that encourages DRY.

For example, consider the following simple class that contains a birth date.

class BirthDate {

    private $birth_date;

    public function getBirthDate($format='Y-m-d') {
        //format $birth_date ...
        //$birth_date = ...
        return $birth_date;
    }

    public function setBirthDate($birth_date) {                   
        //if($birth_date is not valid) throw an exception ...          
        $this->birth_date = $birth_date;
    }

    public function getAge() {
        //calculate age ...
        return $age;
    }

    public function getDaysUntilBirthday() {
        //calculate days until birth days
        return $days;
    }
}

You'll want to validate that the value being set is

  • A valid date
  • Not in the future

And you don't want to do this validation all over your application (or over multiple applications for that matter). Instead, it's easier to make the member variable protected or private (in order to make the setter the only access point) and to validate in the setter because then you'll know that the object contains a valid birth date no matter which part of the application the object came from and if you want to add more validation then you can add it in a single place.

You might want to add multiple formatters that operate on the same member variable i.e. getAge() and getDaysUntilBirthday() and you might want to enforce a configurable format in getBirthDate() depending on locale. Therefore I prefer consistently accessing values via getters as opposed to mixing $date->getAge() with $date->birth_date.

getters and setters are also useful when you extend objects. For example, suppose your application needed to allow 150+ year birth dates in some places but not in others. One way to solve the problem without repeating any code would be to extend the BirthDate object and put the additional validation in the setter.

class LivingBirthDate extends BirthDate {

    public function setBirthDate($birth_date) {
        //if $birth_date is greater than 150 years throw an exception
        //else pass to parent's setter
        return parent::setBirthDate($birth_date);
    }
}
俏︾媚 2024-10-15 17:34:45

这篇文章并不是专门讨论 __get__set 而是 __call,除了方法调用之外,它们的想法是相同的。一般来说,由于评论和帖子中概述的原因,我会远离任何类型的允许重载的魔术方法但是,我最近遇到了我使用的第 3 方 API,它使用 SERVICE和子服务,例如:

http://3rdparty.api.com?service=APIService.doActionOne&apikey=12341234

其中重要的部分是,除了子操作之外,该 API 具有相同的所有内容,在本例中为 doActionOne。这个想法是,开发人员(我自己和其他使用此类的人)可以通过名称调用子服务,而不是像这样:

$myClass->doAction(array('service'=>'doActionOne','args'=>$args));

我可以这样做:

 $myClass->doActionOne($args);

硬编码这只会导致大量重复(这个例子非常 与代码大致相似):

public function doActionOne($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

public function doActionTwo($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

public function doActionThree($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

protected function executeCoreCall($service)
    {
        $cURL = new \cURL();
        return $cURL->('http://3rdparty.api.com?service='.$service.'&apikey='.$this->api.'&'.http_build_query($this->args))
                    ->getResponse();
    }

但是通过 __call() 的神奇方法,我能够使用动态方法访问所有服务:

public function __call($name, $arguments)
    {
        $this->args     =   $arguments;
        $this->response =   $this->executeCoreCall("APIService.{$name}");   
        return $this;
    }

这种动态调用返回数据的好处是如果供应商添加另一个子服务,我不必在类中添加另一个方法或创建扩展类等。我不确定这对任何人是否有用,但我想我会展示一个示例,其中 __set__get__call等可能是一个可以考虑的选项,因为主要功能是返回数据。


编辑:

巧合的是,我在发布几天后看到了这个,它准确地概述了我的情况。这不是我指的 API,但方法的应用是相同的:

我使用 api正确吗?

This post is not specifically about __get and __set but rather __call which is the same idea except for method calling. As a rule, I stay away from any type of magic methods that allow for overloading for reasons outlined in the comments and posts HOWEVER, I recently ran into a 3rd-party API that I use which uses a SERVICE and a SUB-SERVICE, example:

http://3rdparty.api.com?service=APIService.doActionOne&apikey=12341234

The important part of this is that this API has everything the same except the sub-action, in this case doActionOne. The idea is that the developer (myself and others using this class) could call the sub-service by name as opposed to something like:

$myClass->doAction(array('service'=>'doActionOne','args'=>$args));

I could do instead:

 $myClass->doActionOne($args);

To hardcode this would just be a lot of duplication (this example very loosely resembles the code):

public function doActionOne($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

public function doActionTwo($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

public function doActionThree($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

protected function executeCoreCall($service)
    {
        $cURL = new \cURL();
        return $cURL->('http://3rdparty.api.com?service='.$service.'&apikey='.$this->api.'&'.http_build_query($this->args))
                    ->getResponse();
    }

But with the magic method of __call() I am able to access all services with dynamic methods:

public function __call($name, $arguments)
    {
        $this->args     =   $arguments;
        $this->response =   $this->executeCoreCall("APIService.{$name}");   
        return $this;
    }

The benefit of this dynamic calling for the return of data is that if the vendor adds another sub-service, I do not have to add another method into the class or create an extended class, etc. I am not sure if this is useful to anyone, but I figured I would show an example where __set, __get, __call, etc. may be an option for consideration since the primary function is the return of data.


EDIT:

Coincidentally, I saw this a few days after posting which outlines exactly my scenario. It is not the API I was referring to but the application of the methods is identical:

Am I using api correctly?

夜灵血窟げ 2024-10-15 17:34:45

更新:不要使用这个答案,因为这是我在学习时发现的非常愚蠢的代码。只需使用普通的 getter 和 setter,就好多了。


我通常使用该变量名作为函数名,并向该函数添加可选参数,以便当调用者填充该可选参数时,然后将其设置为属性并返回 $this 对象(链接),然后当该可选参数未指定时呼叫者,我只是将财产返回给呼叫者。

我的例子:

class Model
{
     private $propOne;
     private $propTwo;

     public function propOne($propVal = '')
     {
          if ($propVal === '') {
              return $this->propOne;
          } else {
              $this->propOne = $propVal;
              return $this;
          }
     }

     public function propTwo($propVal = '')
     {
          if ($propVal === '') {
              return $this->propTwo;
          } else {
              $this->propTwo = $propVal;
              return $this;
          }
     }
}

Update: Don't use this answer since this is very dumb code that I found while I learn. Just use plain getter and setter, it's much better.


I usually using that variable name as function name, and add optional parameter to that function so when that optional parameter is filled by caller, then set it to the property and return $this object (chaining) and then when that optional parameter not specified by caller, i just return the property to the caller.

My example:

class Model
{
     private $propOne;
     private $propTwo;

     public function propOne($propVal = '')
     {
          if ($propVal === '') {
              return $this->propOne;
          } else {
              $this->propOne = $propVal;
              return $this;
          }
     }

     public function propTwo($propVal = '')
     {
          if ($propVal === '') {
              return $this->propTwo;
          } else {
              $this->propTwo = $propVal;
              return $this;
          }
     }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文