php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #67851 Extending method visibility with a trait does not work with interfaces
Submitted: 2014-08-16 22:41 UTC Modified: 2015-04-15 14:01 UTC
From: awoody_02 at hotmail dot com Assigned:
Status: Not a bug Package: Class/Object related
PHP Version: 5.5.15 OS: Windows 7 Ultimate SP1
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: awoody_02 at hotmail dot com
New email:
PHP Version: OS:

 

 [2014-08-16 22:41 UTC] awoody_02 at hotmail dot com
Description:
------------
Consider a class that implements an interface, thereby requiring a public method, and that inherits a protected or private implementation of that method from a base class.

Normally it is possible to extend the visibility of the inherited method by overriding it with a public method of the same name that simply defers the call to parent::method().

However if this overriding method is imported from a trait, PHP issues a fatal error complaining that the visibility of the method in the base class is insufficient to meet the requirements of the interface.

Removing the interface requirement from the class and using Reflection shows that it successfully imports the public method from the trait and overrides the protected/private inherited method, so the bug must be in the checking for consistency with the interface. The line on which the error is reported is the line that contains the opening brace of the class definition that throws the error.

Test script:
---------------
interface IReadOnlyObject
{
	public function GetValue();
}

interface IWritableObject extends IReadOnlyObject
{
	public function SetValue($value);
}

trait ReadOnlyObjectMethods
{
	private $value = null;

	public function GetValue()
	{
		return $this->value;
	}

	protected function SetValue($value)
	{
		$this->value = $value;
	}
}

trait WritableObjectMethods
{
	public function SetValue($value)
	{
		parent::SetValue($value);
	}
}

class ReadOnlyObject implements IReadOnlyObject
{
	use ReadOnlyObjectMethods;
}

class WritableObject extends ReadOnlyObject implements IWritableObject
{
	use WritableObjectMethods;
}

$o = new WritableObject();
$o->SetValue('hello');
echo $o->GetValue();

Expected result:
----------------
hello

Actual result:
--------------
Fatal error: Access level to ReadOnlyObject::SetValue() must be public (as in class IWritableObject) in test.php on line 42

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-04-15 13:31 UTC] jpauli@php.net
You hit a tricky behavior of traits (and they provide many of them).

What happens to you, is that when you declare WritableObject, the interface will be honored BEFORE the trait (it is always the case).

The trait declares the method as public, but at the time the interface is honnored (bound), the trait is still unknown to the class, thus the setValue() into it is still private at that moment (from its inheritence).

I don't think we can fix this in PHP5, but we can work on those tricky scenarios for PHP7
 [2015-04-15 14:00 UTC] jpauli@php.net
-Status: Open +Status: Not a bug
 [2015-04-15 14:00 UTC] jpauli@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php


 [2015-04-15 14:01 UTC] jpauli@php.net
I closed as "not a bug", as actually, this is how things are done, and we can't change that in PHP5.

Interfaces are honored before traits, which may lead to conflicts.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 15:01:28 2024 UTC