php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #46506 readonly attribute for (public) class variables
Submitted: 2008-11-06 14:11 UTC Modified: 2018-09-05 16:16 UTC
Votes:87
Avg. Score:4.7 ± 0.7
Reproduced:75 of 77 (97.4%)
Same Version:37 (49.3%)
Same OS:32 (42.7%)
From: glideraerobatics at hotmail dot com Assigned:
Status: Suspended Package: *General Issues
PHP Version: 5.4 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: glideraerobatics at hotmail dot com
New email:
PHP Version: OS:

 

 [2008-11-06 14:11 UTC] glideraerobatics at hotmail dot com
Description:
------------
Here is a description of this feature in C#:
http://blog.paranoidferret.com/index.php/2007/09/12/csharp-tutorial-the-readonly-keyword/

In a nutshell it allows you to create classes with public variables that are readonly and can only be written to / initialized by the contructor of the class itself.

This allows the creation of simple objects without having using accessor methods to protect the internal data that was validated and initialized during construction from corruption.

Note: bug http://bugs.php.net/bug.php?id=39467 is about a similar problem but the proposed solution is not quite right.

Reproduce code:
---------------
class Person
{
  public readonly $name = null;
  public readonly $age = null;
  public readonly $weight = null;

  public function __construct($name, $age, $weight) {
  {
    if (!isAgeToWeightRatioSane($age, $weight)) {
      throw new InvalidArgumentException("Invalid age to weight ratio: $age : $weight");
    }
    // TODO: other sanity checks here.
    $this->name = $name;
    $this->age = $age;
    $this->weight = $weight;
  }
}


$person = new Person('Joe', 22, 100);

$person->age = 33; // throws a yet to be named exception




Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-07-12 13:27 UTC] mickael at lupusmic dot org
I guess the readonly keyword in C# is wrong. It hasn't to disallow variance of the attribute, but the setting from outside the object. Readonly is opposite to constness of a variable. For example, DOMDocument::doctype isn't constant.

What you think about, it's a dynamic const attribute, in opposite to static one.

So said, a readonly is like a const attribute, but isn't. And it doesn't deserve a scope qualifier, it can only be public.

For example :

class thing
{
	readonly	$status = 'instantiation' ;

	public		function __construct()
	{
		// do init stuff

		$this->status = 'instantiate' ;
	}

	public		function invalidate()
	{
		// do stuf
		$this->status = 'invalid' ;
	}
}

// Usage
$o = new thing ;
echo $o->status ; // display 'instantiation'
$o->status = 'forced' ; // throw an error E_FATAL

$o->invalidate() ; // do stuff then set readonly status attribute
 [2012-01-24 23:12 UTC] luke at cywh dot com
I just want the "readonly" keyword to protect a property from being written to 
from the outside. I still want to write to the property from within the class.

Here's a simple example of how it could be used:

class Parent
{
    readonly public $children = array();

    public function addChild($childName)
    {
        $this->children[] = ucwords(strtolower($childName));
    }
}

$parent = new Parent;
$parent->addChild("billy");

$count = count($parent->children); // You can do this

print "Parent has $count children\n";

$name = $parent->children[0]; // You can even do this

print "Parent's first child's name is $name\n";

$parent->children[0] = "BILLY"; // But you can't do this
$parent->children[] = "BOB"; // Or this
$parent->children = NULL; // Or this
unset($parent->children); // Or this


The above example frees you from having to do this:

class Parent
{
    protected $children = array();

    public function addChild($childName)
    {
        $this->children[] = ucwords(strtolower($childName));
    }

    public function hasChild($index)
    {
        return isset($this->children[$index]);
    }

    public function getChild($index)
    {
        return $this->children[$index];
    }

    public function childCount()
    {
        return count($this->children);
    }
}


I've had to write MANY classes like this. The has/isset, get, and count 
functions are virtually all the same.

Some people have even resorted to using __get and __set:

http://stackoverflow.com/questions/402215/php-readonly-properties

The __get and __set magic functions are slow, so much so you're better off 
making your own getters and setters, which is multiplied by the number of 
properties you need like this in the class.

I would recommend the following definitions:

readonly public = read for public, write for protected
readonly protected = read for protected, write for private

I think this should satisfy most cases.
 [2012-01-25 08:11 UTC] glideraerobatics at hotmail dot com
Changed affected PHP version.
 [2012-01-25 08:11 UTC] glideraerobatics at hotmail dot com
-Package: Feature/Change Request +Package: *General Issues -PHP Version: 5.3.0alpha2 +PHP Version: 5.4
 [2012-09-18 21:53 UTC] mat dot barrie at gmail dot com
As a point of interest, the C# readonly keyword mentioned actually does not 
protect exposed classes from being modified, it prevents assignment.  So from 
your example if you duplicate the C# behaviour, this is what it actually would 
work like this, which I don't think is what you're asking for:

--
$count = count($parent->children); // You can do this
$name = $parent->children[0]; // You can even do this

$parent->children[0] = "BILLY"; // You can still do this
$parent->children[] = "BOB"; // And you can still even do this
$parent->children = NULL; // But not this
unset($parent->children); // Or this
--

A readonly attribute probably isn't what's needed here (after all, you're not 
actually asking for a property that can be made readonly) but instead if the 
protection level could be defined on the getter and setter independently, so 
that set could be defined as private and get as public.  __get and __set sort of 
do this, but they're useless if you're serialising, hurt performance, and unless 
I'm missing something you can't add phpDoc comments to the exposed pseudo-
properties.
 [2012-11-27 15:55 UTC] info at strictcoding dot co dot uk
+1 for this awesome feature. Any reviews from the PHP team?
 [2013-03-30 18:40 UTC] stian dot pedersen at gmail dot com
This feature is seconded. Basically it would be useful to have a modifier which 
allows internal modification but disallows public reassignment. By an example, 
letting "property" be the new key word,

class Order
{
   property $customer;
}
class Order
{
   private $customer;
   public getCustomer(){return $this->customer;}
}

$order->customer and $order->getCustomer() could have the same semantics in that 
a "copy of the pointer" in C terms is returned and you cannot call $order-
>customer = null any more than you could call $order->getCustomer() = null. 
However, $customer itself should be modifyable, for instance, $order->customer-
>id = 1000, if id is declared as public. This would be more in tune of mat dot 
barrie at gmail dot com and very useful in OOP.
Inside the class, I would prefer to be able to reassign customer at will (even 
after constructor).

Syntactically it would be nice to chose a syntax that would allow support for 
setters also, but for now, I would be happy with this. It sorta does the same 
thing as the __get() trick but does not mess up IDE support and will probably 
execute faster.
 [2013-03-31 09:09 UTC] glideraerobatics at hotmail dot com
@stian dot pedersen at gmail dot com
A "property" keyword isn't good enough because you still need the scope keywords 
public and protected at least. That's why I used the C# convention in the 1st 
post.
 [2013-07-05 00:37 UTC] dave at shax dot com
'readonly' property as described here would be extremely useful to me 
in daily development.

Doesn't need to be fancy. The most obvious functionality, as described 
by others above, is exactly what I want. At the moment, I duplicate 
this functionality in 90% my classes using __get().

There's presumably a bit of a performance knock in doing it with 
__get() (never benchmarked it, but it seems obvious) - not routing 
the read requests for each property through __get() or a separate 
getter function saves a function call and would be faster. 

Because I currently use __get() on nearly every major object in my 
application, I could easily save several hundred function calls per 
request, which is probably creating a measurable difference in page 
generation.

It may seem CRAZY to accept performance slowdown for something this 
trivial, but for me having each object defined as an individual solid 
API in its own right makes my code much more stable, and in a large 
application prevents unwanted/accidental interference from outside 
modules. It's mainly about being SUPER sure I've internally validated 
all data that's entering each object - hardening it! 

At the moment my options are to make it writable by anyone, or accept 
a performance hit - this is why I'm very keen for this to be added to
the language!

For the implementation of this, I'd want it to be like 'protected', 
not 'private' wherein other classes that inherit the property can 
also write to it.
 [2013-07-05 00:44 UTC] stian dot pedersen at gmail dot com
It is especially useful for IDE. __get and __set work horribly with, for instance, 
PhpStorm, arguably the best current PHP IDE.

I would like to see "type hinting" applied to variables as well.
private Object $object. Currently, i will have to do /** @var Object $object */
above to get correct code completion, which violates DRY and is inelegant. 
Finally, namespace visibility and some stricter language rules, and maybe PHP may 
actually be suited for large application web development.
 [2018-09-05 16:16 UTC] cmb@php.net
-Status: Open +Status: Suspended
 [2018-09-05 16:16 UTC] cmb@php.net
Note that such changes to the language require the RFC process[1].
A few years ago a respective RFC[2] has been withdrawn.  So if
anybody is still interested in having this feature, please read
the RFC and the related discussion[3].  If you still feel this
feature is desireable, please start the RFC process.  For the time
being, I'm suspending this ticket.

[1] <https://wiki.php.net/rfc/howto>
[2] <https://wiki.php.net/rfc/readonly_properties>
[3] <https://externals.io/message/78284>
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Dec 27 00:01:30 2024 UTC