PHP RFC: Encapsulation
- Version: 0.1
- Date: 2015-02-19
- Author: Leigh, leigh@php.net (Draft RFC) & Guilherme Blanco, guilhermeblanco@php.net (Implementation)
- Status: Draft
- First Published at: https://wiki.php.net/rfc/encapsulation
Introduction
This RFC proposes the introduction of encapsulation to classes, interfaces and traits within a namespace.
Proposal
Since PHP 5.3 namespaces have been used as a tool to organise related pieces of functionality into named units. However it is often the case that not all of the functionality contained within a namespace is required to be exposed and accessible to other parts of the program, even if a well documented and complete API is presented a developer may still access the inner-most parts of the module.
With the application of encapsulation a developer trying to directly access functionality that was intended to only be used within the scope of the module (a helper class or utility function) will be denied this access, and it will serve as an indication that they are attempting to do something outside of the intended design.
This proposal is limited to the private visibility of classes, interfaces and traits, reusing the existing private
keyword. There is plenty of room for future scope if the community decides that this type of functionality is desirable on the whole.
New Behaviour
- Private classes can only be instantiated and extended within their defining namespace.
- Private interfaces can only be implemented and extended within their defining namespace.
- Private traits can only be extended and used in classes that are also defined within the same namespace.
- Attempting to access a private class, interface or trait out of scope results in a fatal error.
Unchanged Behaviour
Outside of the new behaviour introduced using the private
keyword, other behaviour remains untouched. For example, once a private class has been instantiated the object can be returned to a caller in any namespace, and the methods and properties of an are accessible just as they would be for a public class. Private classes and interfaces can still be used for parameter hinting in any namespace, and checks such as implements
can be used with them.
Reflection
The following reflection methods have been implemented in the patch
- ReflectionClass::isPublic()
- ReflectionClass::isPrivate()
- ReflectionClass::setAccessible()
Auto-loading
Private visibility is respected when auto-loading is triggered from an invalid namespace.
Examples
Instantiating a private class from an invalid scope
namespace Foo { private class Bar {} function giveMeBar() { return new Bar; } } $foobar = \Foo\giveMeBar(); // This is fine $foobar = new \Foo\Bar; // This is a fatal error
Extending a private class from an invalid scope
namespace Foo { private class Bar {} } namespace Baz { class Qux extends \Foo\Bar {} // This is a fatal error }
Implementing a private interface from an invalid scope
namespace Foo { private interface Bar {} } namespace Baz { class Qux implements \Foo\Bar {} // This is a fatal error }
Backward Incompatible Changes
There are no backward incompatible changes. No new keywords are introduced and the default visibility of classes remains public
.
Proposed PHP Version(s)
7.0
RFC Impact
To SAPIs
All SAPIs are affected equally. They gain the new behaviour and do not suffer any breaks.
To Existing Extensions
No existing extensions are affected, as this behaviour is not available to them yet.
To Opcache
There are no new opcodes, and checks are performed at compile time as well as runtime.
Open Issues
- I didn't see any special handling for
clone
in the patch, and have raised this with Guilherme
Future Scope
There is future scope for similar behaviour to be applied to functions within a namespace, as well as the potential to use the protected
keyword. However as these enhancements are likely to lead to extended debate, they should be covered in a separate RFC.
Proposed Voting Choices
As a change to the functionality of the core language, this RFC requires at least 2/3 of the votes to be in favour of the proposal to pass.
Patches and Tests
A complete implementation is available: https://github.com/php/php-src/pull/947
This is considered to be a near-final patch, unless significant changes are required or bugs identified during the RFC process.