I am reading about design patterns in PHP and I keep seeing the following syntax, for example
abstract class AbstractObserver {
abstract function update(AbstractSubject $subject_in);
}
or
class PatternObserver extends AbstractObserver {
public function __construct() {
}
public function update(AbstractSubject $subject) {
}
}
(code is part of this example)
where "AbstractSubject" is another abstract class.
I am used to defining methods like methodName($var), not including a class name in there, like methodName(className $var).
So, what actually the class name does in a method ? My best guess is that it passes something like a reference in that class? Can you explain to me what it actually does?
Thanks
It's called Type Hinting, since php 7 you can use scalar types to type hint parameters. (Some more)
Related
I have a number of PHP classes inheriting a single base class. This base class offers up a static method, let's call it methodA() that can be overridden in each class, but in practice won't be overridden very much. Each class has a static variable, let's call it name that is different, but methodA() needs to be able to act based on name differently for each class. I'm new to object oriented PHP, so I'm not exactly sure how best to do this.
In Java I would make a property on the base class and then define it in the constructor method which I would then call via super(), but I'm not sure how best to do this based on static data. Am I on the right track? What would be the best way to accomplish this?
This answer was thanks to #AlexBlex.
PHP provides a feature called Late Static Bindings that allow exactly this behavior.
As of PHP 5.3.0, PHP implements a feature called late static bindings which can be used to reference the called class in a context of static inheritance.
In the example provided in the question, the way to do this would be as such:
class Base {
public static function methodA() {
return static::$name;
}
}
class A extends Base {
public static $name = "Apple";
}
class B extends Base {
public static $name = "Box";
}
You can then call the function as such with the expected results:
echo(A::methodA()); // "Apple"
echo(B::methodA()); // "Box"
This example runs in PHP 7.
I'm working on the next version of my language-constraint reconciliation system. I started with this (the abstract function declaration seems to be the problem):
namespace AAABIT;
abstract class LangPrefSet{
private $LangPrefs;
public function __construct(){$this->LangPrefs=array();}
public function add(LangPref $langpref){$this->LangPrefs[]=$langpref;}
public function langPrefs(){return $this->LangPrefs;}
abstract public function reconcile(LangPrefSet $other);//←Seems to be throwing an error… We don't strictly need this line, but this is a little concerning…
protected static function reconcile_LangPrefSets(LangPrefSet_ForUser $UserLangPrefSet,LangPrefSet_Resource $RsrcLangPrefSet,$maxOptions){//…
}
}
//Following classes are necessary because language similarity is a one-way mapping. Just because resources in Lang A (e.g. Russian) are likely to be readily understandable for speakers/readers of Lang B (e.g. Ukrainian), does not mean that the resources in Lang B (e.g. Ukrainian) are equally intelligible for speakers/readers of Lang A (e.g. Russian)!
class LangPrefSet_For_User extends LangPrefSet{public function reconcile(LangPrefSet_Resource $RsrcLangPrefSet){return self::reconcile_LangPrefSets(self,$RsrcLangPrefSet);}}
class LangPrefSet_Resource extends LangPrefSet{public function reconcile(LangPrefSet_For_User $UserLangPrefSet){return self::reconcile_LangPrefSets($UserLangPrefSet,self);}}
I thought this would work because LangPrefSet_Resource conforms to LangPrefSet; but PHP found this objectionable, throwing the aforementioned error. I thought I might have better luck with an Interface… So I did this:
interface LangPrefSet_Reconcilable{
public function reconcile(LangPrefSet_Reconcilable $other);
}
Then I made the two classes extending LangPrefSet, implements LangPrefSet_Reconcilable, and commented out the abstract function declaration (after first trying to make that require an LangPrefSet_Reconcilable interface-typed parameter, also which didn't work) — the results are:
Fatal error: Declaration of AAABIT\LangPrefSet_For_User::reconcile() must be compatible with AAABIT\LangPrefSet_Reconcilable::reconcile(AAABIT\LangPrefSet_Reconcilable $other)
— This is not a blocker issue for me, since I can just strip out the abstract function and interface and the system will work fine. However I'm concerned that I might not have understood interfaces/ abstract classes properly!
What is wrong with specifying that a class method overriding abstract function a(ObjB $b) or a similar interface specification, takes as a parameter ObjC $c, where ObjC extends ObjB?
Your parameter type must be the same in the interface(or abstract class) and where you are implementing the same. right now the interface is LangPrefSet_Reconcilable
and the implementation says LangPrefSet_Resource and LangPrefSet_For_User.
You can either use a 2nd interface like below, or use the same class.
interface LangPrefSetReconcilableDataInteface {
...
}
interface LangPrefSet_Reconcilable{
public function reconcile(LangPrefSetReconcilableDataInteface $other);
}
class LangPrefSet_Resource implements LangPrefSetReconcilableDataInteface {
...
}
class LangPrefSet_For_User implements LangPrefSetReconcilableDataInteface {
...
}
class LangPrefSet_For_User extends LangPrefSet
{
public function reconcile(LangPrefSetReconcilableDataInteface $RsrcLangPrefSet)
{
return self::reconcile_LangPrefSets(self,$RsrcLangPrefSet);
}
}
class LangPrefSet_Resource extends LangPrefSet
{
public function reconcile(LangPrefSetReconcilableDataInteface $UserLangPrefSet)
{
return self::reconcile_LangPrefSets($UserLangPrefSet,self);
}
}
The issue may to be that in the abstract class declaration, I am specifying that any LangPrefSet must be acceptable to the reconcile() object method:
abstract public function reconcile(LangPrefSet $other);
— Whereas in the actual method declaration, I am specifying in one case that only a LangPrefSet_Resource would be acceptable, and in the other case, that only a LangPrefSet_For_User would be acceptable.
The object method declarations are therefore incompatible with the abstract method declaration. It seems that the proper application of these interface/abstract method declaration techniques is not to broaden the range of permissible parameter type constraints in classes that implement the interface, since we may achieve this simply by declaring the input restrictions as they ought to be for those specific classes/methods; but rather, interface/abstract method declarations are meant to broaden the range of parameters that may actually be passed to a method that specifies these abstract parameter types.
I'm comming from C++ and from all I've read in the manual and code examples, none seem to separate a class method declaration from its definition. Is this not possible in PHP? Dosn't this lead to very hard-to-read and cluttery interfaces?
Thanks
EDIT:
I want something like this:
class MyClass
{
public function Foo();
};
MyClass::Foo()
{
echo "O-hoy!";
}
When not using interfaces, you are right. Like in Java, the class definition is the declaration. However, (also like Java), you have the interface available that you can use:
From the documentation:
interface iTemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}
// Implement the interface
// This will work
class Template implements iTemplate
{
private $vars = array();
...
}
it is perfectly legal to put them in different files. The class definition though, will always be in one file. You cannot use the partial keyword as you can in C#.
This concept is not necessary in PHP. If you want to get a clean interface, you might define one. http://php.net/manual/en/language.oop5.interfaces.php
Sorry if the title looks odd, I don't know how to call it. I was inspecting a framework and I wonder how this works?
<?php
//namespace and use
abstract class Model {
//...
public function __call($method,$params){
//some stuff
return static::$$method;
}
}
It's an abstract class, so to what class will static refer to? (considering it's not extending anything) I tried to var_dump method but that method is not in that class. And why does it have a double dollar sign.
EDIT: Oh it will call the __callStatic method. I need pills.
It's called "late static binding" and unlike self, which always refers to the context ("class"), where it is defined, it refers always to the context it is called on.
There was an interesting question in a practice test that I did not understand the answer to. What is the output of the following code:
<?php
class Foo {
public $name = 'Andrew';
public function getName() {
echo $this->name;
}
}
class Bar extends Foo {
public $name = 'John';
public function getName() {
Foo::getName();
}
}
$a = new Bar;
$a->getName();
?>
Initially, I thought this was produce an error because static methods can not reference $this (atleast in PHP5). I tested this myself and it actually outputs John.
I added Foo::getName(); at the end of the script and did get the error I was expecting. So, what changes when you call a static method from within a class that extends the class you're calling from?
Would anyone mind explaining in detail exactly what is going on here?
Foo::getName() is using an older PHP4 style of scope resolution operator to allow an overridden method to be called.
In PHP5 you would use parent::getName() instead
It's useful if you want to extend, rather than completely override the behaviour of the base class, e.g. this might make it clearer
class Bar extends Foo {
public $name = 'John';
public function getName() {
echo "My name is ";
parent::getName();
}
}
If you call the static method bound to the other object, the method is executed in the context of the current object. Which allows access to the $this-object.
Better way to call the superclass-method from inside the subclass would be:
parent::getName();
$this to the object in whose context the method was called. So: $this is $a->getName() is $a. $this in $fooInstance->getName() would be $fooInstance. In the case that $this is set (in an object $a's method call) and we call a static method, $this remains assigned to $a.
Seems like quite a lot of confusion could come out of using this feature. :)
When you call $a->getName() you're referencing a specific object, $a, which is of class Bar and so returns "John".
Foo::getName() isn't valid outside the function because there's no specific object.
I'm not sure it works in PHP, but if you cast the object to the superclass as in (Foo)$a->getName() then you'd get "Andrew" as your result. You'd still be talking about the specific object ($a) but in this case of type Foo. (Note you wouldn't generally want to do this)
Sometimes programmers are better at explaining things in code than in English!
The first thing going on here is the concept of overloading. When you instantiate Bar, it's getName() method overloads the method of the same name in Foo.
Overloading is a powerful and important part of OOD.
However, it is often useful to be able to call the version of a method that exists in the Parent class (Foo).
Here's an example:
class Dog
{
public function getTag()
{
return "I'm a dog.";
}
}
class Skip extends dog
{
public function getTag()
{
return Dog::getTag() . " My name is Skip.";
// I'm using Dog:: because it matches your example. However, you should use parent:: instead.
}
}
$o = new Skip();
echo $o->getTag(); // Echo's: "I'm a dog. My name is Skip."
Clearly this is a very parochial example but it illustrates a point.
Your base class is the most general implementation of a Type. In this case, it's "Dog." You want to put information in this base class that is common to all instances of that Type. This prevents duplication in each of the Derived classes (like "Skip").
Your script is taking advantage of this feature, perhaps inadvertently.