php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #61924 cannot use self in interface function declaration
Submitted: 2012-05-03 14:20 UTC Modified: 2012-05-05 15:31 UTC
From: jenwelsh at yahoo dot com Assigned: laruence (profile)
Status: Not a bug Package: Class/Object related
PHP Version: 5.4.1 OS:
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: jenwelsh at yahoo dot com
New email:
PHP Version: OS:

 

 [2012-05-03 14:20 UTC] jenwelsh at yahoo dot com
Description:
------------
I am reviewing existing code on a PHP5.4.1 testbed. I've discovered that interface declarations using 'self' as a type hint no longer allow implementations to use 'self' but require them to use the interface name.

It is no longer possible for an interface to declare a method that requires the implementor's class as a typehint without declaring that class specifically. And that would limit the usefulness of that interface to one class only.

Test script:
---------------
interface IComparable {
	public function equals(self $other);
}

class A implements IComparable{
	protected $var;
	
	public function __construct(self $v){
		$this->var=$v;
	}
	
	public function equals($other){
		return ($this->var == $other->var) ? 'equal' : 'different';
	}
}

$a1= new A(7);
$a2= new A(5);
$a3= new A(5);

echo $a1->equals($a2),"\n";
echo $a2->equals($a3),"\n";

Expected result:
----------------
different
equal


Actual result:
--------------
PHP Fatal error:  Declaration of A::equals() must be compatible with IComparable::equals(IComparable $other) 

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-05-03 14:45 UTC] laruence@php.net
it's always not allowed since php 5.2,  why are you thinking it worked once?
 [2012-05-03 14:52 UTC] laruence@php.net
or you can change it to a feature request :)
 [2012-05-03 14:52 UTC] laruence@php.net
-Status: Open +Status: Feedback
 [2012-05-03 15:22 UTC] jenwelsh at yahoo dot com
The reason I "think" it did work, is because it is **currently** working on a production site with PHP 5.3.11.  And it **has** been working for over 2 years.
 [2012-05-03 16:48 UTC] laruence@php.net
-Assigned To: +Assigned To: laruence
 [2012-05-03 16:48 UTC] laruence@php.net
Oh, sorry I misunderstanded , assign to my self , should have Sth to do with a fix 
made by me
 [2012-05-04 00:48 UTC] laruence@php.net
Actually, I think it's a improvement of PHP-5.4, this change is introduced by 
fixing this issue: https://bugs.php.net/bug.php?id=60573

before this, you declare 
class A implements IComparable{
	public function equals(self $other){
		return ($this->var == $other->var) ? 'equal' : 'different';
	}
}

actullay the self in here is A, not IComparable. 

but in the IComparable, the self means IComparable itsself.

In scrupulously, A is_a Icomparable, but not equal to Icomperable, what do you 
think? 

thanks
 [2012-05-04 02:18 UTC] colder@php.net
The rule was previously accepted as the type hints where simple syntactic check 
(basically string comparisons). This was wrong, and got fixed in 5.4.

You cannot use self/parent as type hints as they depend on the current type in a 
covariant fashion, and type hints need to be contravariant.

To explicit the problem in 5.3:

interface A { public function foo(self $a); } 
class B implements A { public function foo(self $a) { } }
class C implements A { public function foo(self $a) { } }


If B (and C) are  valid (per <php5.4 rules) implementations of A, it means that 
anything you are allowed to do on A should work on B (and C) (by Liskov's 
substitution principle)

now let's see:

function test(A $a) { $a->foo(new C); }

test(new B) will fail
test(new C) will work

A side effect from this fix is that the wrong typehint now fails with an error, 
since B/C are invalid implementations of A.
 [2012-05-04 02:24 UTC] colder@php.net
Just to make it clear, you can use self/parent as type hints, as long as the 
class they reference is right, for instance:

class A {
    public function foo(self $plop) { }
}

class B extends A {
    public function foo(A $plop) { }
}

class C extends A {
    public function foo(parent $plop) { } 
}

are all equally fine. since parent in C is A, and self in A is A.
 [2012-05-04 02:24 UTC] colder@php.net
-Status: Feedback +Status: Not a bug
 [2012-05-04 15:00 UTC] jenwelsh at yahoo dot com
I am in the unfortunate situation of agreeing that you are technically correct. 

But I must say that it makes using "self" as a typehint fairly useless in an interface. Now if I want to require that a method argument be same type as $this, I'll have to do it this way:

interface IComparable {
	public function equals($other);
}

class A implements IComparable{
	protected $var;
	function equals($other) {
		if(get_class($other)!== get_class($this)) throw Exception('wrong arg class');
		return $this->var==$other->var;
	}
}

And that will make the interface much less specific and strong.
 [2012-05-05 15:31 UTC] colder@php.net
You are confusing "specific and strong" with wrong. With self as a type hint, 
you make the type system unsound, which means that it will provide you with 
absolutely no (virtual) guarantees.

For example:
function isEqual(IComparable $a, IComparable $b) {
   return $a->equals($b);
}

even though the IComparable interface declares a equals function that takes a 
IComparable, this code is not valid if self typehints work the way you want.


Another design problem of your example is:

equals should be reflective, that is: $a->equals($b) should mean $b->equals($a); 
This fails to be the case if self typehints are possible:

class A extends IComparable {
   def equals(self $a) { return true; }
}
class B extends A {
   def equals(self $a) { return true; }
}
$a = new A; $b = new B;

$a->equals($b); // OK
$b->equals($a); // type error
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 17:01:58 2024 UTC