Bug #63659 [Opn->Csd]: Type hint 'self' means plain 'self' instead of original target class

From: Date: Sat, 07 Mar 2015 10:50:16 +0000
Subject: Bug #63659 [Opn->Csd]: Type hint 'self' means plain 'self' instead of original target class
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-191231@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=63659&edit=1

 ID:                 63659
 Updated by:         reeze@php.net
 Reported by:        vovan-ve at yandex dot ru
 Summary:            Type hint 'self' means plain 'self' instead of
                     original target class
-Status:             Open
+Status:             Closed
 Type:               Bug
 Package:            Class/Object related
 PHP Version:        Irrelevant
-Assigned To:        
+Assigned To:        reeze
 Block user comment: N
 Private report:     N

 New Comment:

It has been fixed since 5.4.1   http://3v4l.org/9iTrD


Previous Comments:
------------------------------------------------------------------------
[2012-11-30 13:58:12] vovan-ve at yandex dot ru

Description:
------------
It seems to me, there is some unclear situation with keywords «self»
(and «parent»). At least this keywords has no detailed documentation.

Imaging following interface:

	interface IFoo {
		public function lorem(IFoo $foo);
		public function ipsum(self $foo);
	}

The first method lorem() wants an implementation of IFoo as argument. The one
problem here is a duplication of the name IFoo inside itself. Usage of keyword
«self» should prevent this "problem", but in theory only.

The second method ipsum() extects an instance of... of what? In fact, it wants
an instance of «self» - that is an instance of implementation class, which
implements or even overrides the method later.

The problem is the fact, that keyword «self» means SELF, not a class or
an interface, there it is writen. We cannot change «self» to «IFoo» in
implementation:

	interface IFoo {
		public function ipsum(self $foo);
	}

	class Fail implements IFoo {
		public function ipsum(IFoo $foo) {
		}
	}
	// Error: Fail::ipsum() must be compatible with IFoo::ipsum()

The ReflectionClass shows «IFoo», «self» and «parent» for corresponding methods:

	interface IFoo {
		public function lorem(IFoo $foo);
		public function ipsum(self $foo);
		public function dolor(parent $foo);
	}

	$ref = new ReflectionClass('IFoo');
	echo $ref;

Output:

    Method [ <user> abstract public method lorem ] {
      - Parameters [1] {
        Parameter #0 [ <required> IFoo $foo ]
      }
    }
    Method [ <user> abstract public method ipsum ] {
      - Parameters [1] {
        Parameter #0 [ <required> self $foo ]
      }
    }
    Method [ <user> abstract public method dolor ] {
      - Parameters [1] {
        Parameter #0 [ <required> parent $foo ]
      }
    }

When we write «self» somewhere, we mean that thing, where «self» is writen.
I think, that method ipsum() should be compiled to receive instance of IFoo
and nothing else. The method dolor() shoult to emit fatal error, because IFoo
has no parent.

If I mistake with that deep meaning if the keywords «self» and «parent»,
and this isn't a bug, I sure this keywords should be documented properly.

Test script:
---------------
interface IFoo {
	public function ipsum(self $foo);
}

class Bar implements IFoo {
	public function ipsum(self $foo) {
		echo __METHOD__, "(", get_class($foo), ")\n";
	}
}

class Baz extends Bar {
	public function ipsum(self $foo) {
		echo __METHOD__, "(", get_class($foo), ")\n";
	}
}

class Lol implements IFoo {
	public function ipsum(self $foo) {
		echo __METHOD__, "(", get_class($foo), ")\n";
	}
}

# test objects
$bar = new Bar();
$baz = new Baz();
$lol = new Lol();

# lets try everything

# IFoo::ipsum(self $foo)

# Bar::ipsum(self $foo)
$bar->ipsum($bar); # Bar::ipsum(Bar)
$bar->ipsum($baz); # Bar::ipsum(Baz)
$bar->ipsum($lol); # Error: Bar::ipsum() extects Bar, but Lol given

# Baz::ipsum(self $foo)
$baz->ipsum($bar); # Error: Baz::ipsum() extects Baz, but Bar given
$baz->ipsum($baz); # Baz::ipsum(Baz)
$baz->ipsum($lol); # Error: Baz::ipsum() extects Baz, but Lol given

# Lol::ipsum(self $foo)
$lol->ipsum($bar); # Error: Lol::ipsum() extects Lol, but Bar given
$lol->ipsum($baz); # Error: Lol::ipsum() extects Lol, but Baz given
$lol->ipsum($lol); # Lol::ipsum(Lol)

Expected result:
----------------
Bar::ipsum(Bar)
Bar::ipsum(Baz)
Bar::ipsum(Lol)
Baz::ipsum(Bar)
Baz::ipsum(Baz)
Baz::ipsum(Lol)
Lol::ipsum(Bar)
Lol::ipsum(Baz)
Lol::ipsum(Lol)

Actual result:
--------------
Bar::ipsum(Bar)
Bar::ipsum(Baz)
Catchable fatal error:  Argument 1 passed to Bar::ipsum() must be an instance of Bar, instance of
Lol given, called in ... and defined ...



------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=63659&edit=1


Thread (2 messages)

« previous php.bugs (#191231) next »