Getter和Setter?
我不是一个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; } }
或只是公共领域:
class MyClass { public $firstField; public $secondField; }
谢谢
你可以使用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; } } ?>
为什么使用getters和setter?
- 可伸缩性 :比search项目代码中的所有var分配更容易重构getter。
- debugging :您可以在设置者和获取者处放置断点。
- 清理 :魔术function不是很好的解决scheme,你的IDE不会提示代码。 更好地使用快速写作获取器的模板。
Google已经发布了PHP的优化指南,结论是:
没有getter和setter 优化PHP
不,你不能使用魔法方法 。 对于PHP,魔法是邪恶的。 为什么?
- 很难debugging。
- 存在性能影响。
- 添加更多代码。
PHP既不是Java,也不是C ++或C#,PHP是不同的,扮演着不同的angular色。
封装在任何OO语言中都很重要,普及与它无关。 在dynamictypes的语言中,比如PHP,它是非常有用的,因为没有使用setter的方法来确保一个属性是一个特定的types。
在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
)也可以,但只有在访问比当前范围具有更低可见性的属性时才可以访问。 在尝试debugging时,如果使用不当,很容易让人头疼。
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
准备!
如果您主张使用__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; } }
除了在这里已经很好和受人尊重的答案,我想扩大PHP没有setter / getters。
PHP没有getter和setter语法 。 它提供了subclassed或magic方法来允许“挂钩”并重写属性查找过程,正如Dave指出的那样。
魔术允许我们懒惰的程序员在我们积极参与项目的时候用更less的代码来做更多的事情,并且通常会以牺牲可读性为代价。
性能每个不必要的函数,在PHP中强制使用类似getter / setter的代码体系结构,在调用时会涉及到自己的内存堆栈,并浪费CPU周期。
可读性:代码库会产生膨胀的代码行,这会影响代码导航,因为更多的LOC意味着更多的滚动。
偏好:就我个人的经验而言,只要当时没有明显的长远利益,我就把静态代码分析的失败作为避免走上神奇之路的一个标志。
误区:
一个共同的论点是可读性。 例如, $someobject->width
比$someobject->width()
更容易阅读。 然而,不像行星的circumference
或width
(可以认为是static
,对象的实例(如$someobject
,需要宽度函数)可能需要测量对象的实例宽度。
因此,可读性增加的主要原因是自信的命名scheme,而不是隐藏输出给定属性值的function。
__get / __set用途:
-
财产价值的预先validation和预先卫生
-
string例如
" some {mathsobj1->generatelatex} multi line text {mathsobj1->latexoutput} with lots of variables for {mathsobj1->generatelatex} some reason "
在这种情况下,
generatelatex
将遵循actionname + methodname的命名scheme -
特殊,明显的情况
$dnastringobj->homeobox($one_rememberable_parameter)->gattaca->findrelated() $dnastringobj->homeobox($one_rememberable_parameter)->gttccaatttga->findrelated()
注意: PHPselect不执行getter / setter语法。 我不是声称getter / setter通常是坏的。
那么,PHP确实有魔术方法__get,__set,__isset&__unset,这始终是一个开始。 唉适当(得到它?)OO属性不仅仅是魔法方法。 PHP实现的主要问题是魔术方法被称为所有不可访问的属性。 这意味着在确定名称实际上是对象的属性时,必须在魔术方法中重复自己(例如,通过调用property_exists())。 你不能真正解决这个基类的一般问题,除非所有的类inheritance自ie。 ClassWithProperties,因为PHP缺乏多重inheritance。
相比之下,Python的新风格的类给你的财产(),它可以让你显式定义所有的属性。 C#有特殊的语法。 http://en.wikipedia.org/wiki/Property_(programming);
我使用魔术方法__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]; } }
只要在你的类中添加上面的方法,现在你可以input:
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
通过这种方式,如果您的课程中存在所有类别,则可以将其设置为/如果您只需要一些特定元素,则可以使用“白名单”作为filter。
例:
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”和“费用”。
您也可以使用“白名单”分配自定义名称来访问您的variables。
例如,
private $callWhiteList = array( "myfoo" => "foo", "zim" => "bom", // ... );
有了这个列表,你现在可以input:
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
。
。
。
就这样。
Doc:在对象上下文中调用不可访问的方法时触发__call() 。
在阅读其他build议后,我倾向于这样说:
作为一个GENERIC规则,你不会总是定义所有属性的设置器,特别是“内部”(信号量,内部标志…)。 只读属性显然不会有setter,所以有些属性只有getter; 这就是__get()来缩小代码的地方:
- 为类似的所有属性定义一个__get()(神奇的全局获取器)
- 将它们分组在数组中如此:
- 他们会分享共同的特点:货币价值将会/可能会正确格式化,具体布局(ISO,美国,国际)等date。
- 代码本身可以validation只有现有的&允许的属性正在阅读使用这个神奇的方法。
- 每当你需要创build一个新的类似属性时,只需声明它并将其名称添加到适当的数组中即可完成。 这比定义一个新的getter 更快 ,也许在代码中一遍又一遍地重复一些代码。
是! 我们也可以编写一个私有方法来做到这一点,但是,然后我们会声明很多方法(++内存),最终调用另一个,总是相同的方法。 为什么不写一个单一的方法来统治他们 …? [没错! 双关绝对有意! :)]
魔法制定者也可以只响应特定的属性,所以所有的datetypes属性可以在一个方法中单独对无效值进行筛选。 如果datetypes属性列在数组中,则可以轻松定义其设置者。 当然,只是一个例子。 有太多的情况。
关于可读性 …呃…这是另外一个争论:我不喜欢被IDE使用(实际上,我不使用它们,他们倾向于告诉我(并强迫我)如何写…我喜欢编码“美”)。 我倾向于命名的一致性,所以使用ctags和其他一些辅助工具对我来说已经足够了。无论如何:一旦所有这些魔术制定者和获得者都完成了,我就会写出其他过于具体或“特殊”的制定者在__set()方法中被推广。 这涵盖了所有我需要获取和设置属性。 当然,并不总是有一个共同点,或者有这么几个属性不值得编写一个神奇的方法的麻烦,然后仍然是旧的好传统的二传手/吸气剂对。
编程语言就是:人为的人造语言。 因此,他们每个人都有自己的语调或口音,语法和风格,所以我不会假装使用与Java或C#相同的“重音”来编写Ruby或Python代码,也不会编写JavaScript或PHP来模拟Perl或SQL …使用它们的方式来使用它们。
一般来说,第一种方法是比较stream行的,因为那些具有以前的编程知识的人可以很容易地转换到PHP,并以面向对象的方式完成工作。 第一种方式更普遍。 我的build议是坚持多种语言的尝试和真实。 那么,如果你使用另外一种语言,你就会做好准备( 而不是花时间重新开始 )。
有许多方法可以在netbeans-convention中创build源代码。 这很好。 它使认为这样easyer === FALSE。 只要使用传统,特别是如果你不确定哪一个属性应该封装,哪一个不是。 我知道,这是一个boi …. pla代码,但对于debugging工作和许多其他人认为这是更好,更清晰的方式。 不要花太多的时间与艺术如何使简单的getter和setters。 如果你使用魔法,你不能实现一些像demeter-rule等devise模式。 在特定情况下,您可以使用magic_calls或小型,快速和清晰的解决scheme。 当然,你也可以用这种方式为devise师制定解决scheme,但是为什么让你活得更加困难。
validation+格式/派生值
Setter让你validation数据,getter让你格式化或派生数据。 对象允许您将数据及其validation和格式化代码封装到鼓励DRY的整齐包中。
例如,考虑以下包含出生date的简单类。
class BirthDate { private $birth_date; public function getBirthDate($format='Ym-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; } }
您将要validation正在设置的值
- 有效的date
- 不在将来
而且你不想在整个应用程序(或者多个应用程序)上进行validation。 相反,使成员variablesprotected或private(为了使setter成为唯一的访问点)更容易,并在setter中进行validation,因为那样你就会知道该对象包含有效的出生date,不pipe哪个部分应用程序的对象来自,如果你想添加更多的validation,那么你可以添加在一个地方。
您可能需要添加多个对相同成员variablesgetAge()
和getDaysUntilBirthday()
进行操作的格式化程序,您可能需要根据语言环境在getBirthDate()
强制执行可configuration的格式。 因此,我倾向于通过getters来一致访问值,而不是将$date->getAge()
与$date->birth_date
。
getters和setter在扩展对象时也很有用。 例如,假设您的应用程序需要在一些地方允许150年以上的出生date,但在其他地方则不允许。 解决问题的一种方法是不用重复任何代码就可以扩展BirthDate
对象,并将其他validation放在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); } }
这篇文章不是专门关于__get
和__set
,而是__call
,除了方法调用之外,这是相同的想法。 作为一个规则,我远离任何types的魔法方法,允许超载的原因在评论和post中概述, 但是 ,我最近遇到了我使用的第三方API使用服务和子服务,例如:
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()
的魔术方法,我可以通过dynamic方法访问所有的服务:
public function __call($name, $arguments) { $this->args = $arguments; $this->response = $this->executeCoreCall("APIService.{$name}"); return $this; }
这种dynamic调用数据返回的好处是,如果供应商添加了另一个子服务,我不必向该类中添加另一个方法或创build一个扩展类等。我不确定这是否有用任何人,但我想我会显示一个例子,其中__set
, __get
__set
, __get
等可能是一个select的考虑,因为主要function是数据的返回。
编辑:
巧合的是,我在发布几天后看到这个概述了我的场景。 这不是我所指的API,但是这些方法的应用是相同的:
我正确使用api吗?
PHP中的getter和setter
<?php class Car { public $color; public $model; public function Model() { return $this->color; } public function New_Model(){ return $this->model = "BMW CAR HERE"; } } $obj = new Car(); echo $obj->color = "red"; //set color in $color variable echo $obj->New_Model(); //get value from New_Model function echo $obj->Model("red"); //set color in function Model ?>
我通常使用该variables名称作为函数名称,并将可选参数添加到该函数,所以当该可选参数由调用者填充时,然后将其设置为该属性并返回$ 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; } } }