PHP 8.4.0 RC3 available for testing

Visibility

The visibility of a property, a method or (as of PHP 7.1.0) a constant can be defined by prefixing the declaration with the keywords public, protected or private. Class members declared public can be accessed everywhere. Members declared protected can be accessed only within the class itself and by inheriting and parent classes. Members declared as private may only be accessed by the class that defines the member.

Property Visibility

Class properties may be defined as public, private, or protected. Properties declared without any explicit visibility keyword are defined as public.

Example #1 Property declaration

<?php
/**
* Define MyClass
*/
class MyClass
{
public
$public = 'Public';
protected
$protected = 'Protected';
private
$private = 'Private';

function
printHello()
{
echo
$this->public;
echo
$this->protected;
echo
$this->private;
}
}

$obj = new MyClass();
echo
$obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private


/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// We can redeclare the public and protected properties, but not private
public $public = 'Public2';
protected
$protected = 'Protected2';

function
printHello()
{
echo
$this->public;
echo
$this->protected;
echo
$this->private;
}
}

$obj2 = new MyClass2();
echo
$obj2->public; // Works
echo $obj2->protected; // Fatal Error
echo $obj2->private; // Undefined
$obj2->printHello(); // Shows Public2, Protected2, Undefined

?>

Asymmetric Property Visibility

As of PHP 8.4, properties may also have their visibility set asymmetrically, with a different scope for reading (get) and writing (set). Specifically, the set visibility may be specified separately, provided it is not more permissive than the default visibility.

Example #2 Asymmetric Property visibility

<?php
class Book
{
public function
__construct(
public private(
set) string $title,
public protected(
set) string $author,
protected private(
set) int $pubYear,
) {}
}

class
SpecialBook extends Book
{
public function
update(string $author, int $year): void
{
$this->author = $author; // OK
$this->pubYear = $year; // Fatal Error
}
}

$b = new Book('How to PHP', 'Peter H. Peterson', 2024);

echo
$b->title; // Works
echo $b->author; // Works
echo $b->pubYear; // Fatal Error

$b->title = 'How not to PHP'; // Fatal Error
$b->author = 'Pedro H. Peterson'; // Fatal Error
$b->pubYear = 2023; // Fatal Error
?>

There are a few caveats regarding asymmetric visibility:

  • Only typed properties may have a separate set visibility.
  • The set visibility must be the same as get or more restrictive. That is, public protected(set) and protected protected(set) are allowed, but protected public(set) will cause a syntax error.
  • If a property is public, then the main visibility may be omitted. That is, public private(set) and private(set) will have the same result.
  • A property with private(set) visibility is automatically final, and may not be redeclared in a child class.
  • Obtaining a reference to a property follows set visibility, not get. That is because a reference may be used to modify the property value.
  • Similarly, trying to write to an array property involves both a get and set operation internally, and therefore will follow the set visibility, as that is always the more restrictive.

When a class extends another, the child class may redefine any property that is not final. When doing so, it may widen either the main visibility or the set visibility, provided that the new visibility is the same or wider than the parent class. However, be aware that if a private property is overridden, it does not actually change the parent's property but creates a new property with a different internal name.

Example #3 Asymmetric Property inheritance

<?php
class Book
{
protected
string $title;
public protected(
set) string $author;
protected private(
set) int $pubYear;
}

class
SpecialBook extends Book
{
public protected(
set) $title; // OK, as reading is wider and writing the same.
public string $author; // OK, as reading is the same and writing is wider.
public protected(set) int $pubYear; // Fatal Error. private(set) properties are final.
}
?>

Method Visibility

Class methods may be defined as public, private, or protected. Methods declared without any explicit visibility keyword are defined as public.

Example #4 Method Declaration

<?php
/**
* Define MyClass
*/
class MyClass
{
// Declare a public constructor
public function __construct() { }

// Declare a public method
public function MyPublic() { }

// Declare a protected method
protected function MyProtected() { }

// Declare a private method
private function MyPrivate() { }

// This is public
function Foo()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate();
}
}

$myclass = new MyClass;
$myclass->MyPublic(); // Works
$myclass->MyProtected(); // Fatal Error
$myclass->MyPrivate(); // Fatal Error
$myclass->Foo(); // Public, Protected and Private work


/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// This is public
function Foo2()
{
$this->MyPublic();
$this->MyProtected();
$this->MyPrivate(); // Fatal Error
}
}

$myclass2 = new MyClass2;
$myclass2->MyPublic(); // Works
$myclass2->Foo2(); // Public and Protected work, not Private

class Bar
{
public function
test() {
$this->testPrivate();
$this->testPublic();
}

public function
testPublic() {
echo
"Bar::testPublic\n";
}

private function
testPrivate() {
echo
"Bar::testPrivate\n";
}
}

class
Foo extends Bar
{
public function
testPublic() {
echo
"Foo::testPublic\n";
}

private function
testPrivate() {
echo
"Foo::testPrivate\n";
}
}

$myFoo = new Foo();
$myFoo->test(); // Bar::testPrivate
// Foo::testPublic
?>

Constant Visibility

As of PHP 7.1.0, class constants may be defined as public, private, or protected. Constants declared without any explicit visibility keyword are defined as public.

Example #5 Constant Declaration as of PHP 7.1.0

<?php
/**
* Define MyClass
*/
class MyClass
{
// Declare a public constant
public const MY_PUBLIC = 'public';

// Declare a protected constant
protected const MY_PROTECTED = 'protected';

// Declare a private constant
private const MY_PRIVATE = 'private';

public function
foo()
{
echo
self::MY_PUBLIC;
echo
self::MY_PROTECTED;
echo
self::MY_PRIVATE;
}
}

$myclass = new MyClass();
MyClass::MY_PUBLIC; // Works
MyClass::MY_PROTECTED; // Fatal Error
MyClass::MY_PRIVATE; // Fatal Error
$myclass->foo(); // Public, Protected and Private work


/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// This is public
function foo2()
{
echo
self::MY_PUBLIC;
echo
self::MY_PROTECTED;
echo
self::MY_PRIVATE; // Fatal Error
}
}

$myclass2 = new MyClass2;
echo
MyClass2::MY_PUBLIC; // Works
$myclass2->foo2(); // Public and Protected work, not Private
?>

Visibility from other objects

Objects of the same type will have access to each others private and protected members even though they are not the same instances. This is because the implementation specific details are already known when inside those objects.

Example #6 Accessing private members of the same object type

<?php
class Test
{
private
$foo;

public function
__construct($foo)
{
$this->foo = $foo;
}

private function
bar()
{
echo
'Accessed the private method.';
}

public function
baz(Test $other)
{
// We can change the private property:
$other->foo = 'hello';
var_dump($other->foo);

// We can also call the private method:
$other->bar();
}
}

$test = new Test('test');

$test->baz(new Test('other'));
?>

The above example will output:

string(5) "hello"
Accessed the private method.
add a note

User Contributed Notes 6 notes

up
62
pgl at yoyo dot org
9 years ago
Just a quick note that it's possible to declare visibility for multiple properties at the same time, by separating them by commas.

eg:

<?php
class a
{
protected
$a, $b;

public
$c, $d;

private
$e, $f;
}
?>
up
4
alperenberatdurmus at gmail dot com
1 year ago
Dynamic properties are "public".
<?php
class MyClass {
public function
setProperty($value) {
$this->dynamicProperty = $value;
}
}
$obj = new MyClass();
$obj->setProperty('Hello World');
echo
$obj->dynamicProperty; // Outputs "Hello World"
?>

This usage is the same as well:
<?php
class MyClass {
}
$obj = new MyClass();
$obj->dynamicProperty = 'Hello World';
echo
$obj->dynamicProperty; // Outputs "Hello World"
?>
up
13
jc dot flash at gmail dot com
12 years ago
if not overwritten, self::$foo in a subclass actually refers to parent's self::$foo
<?php
class one
{
protected static
$foo = "bar";
public function
change_foo($value)
{
self::$foo = $value;
}
}

class
two extends one
{
public function
tell_me()
{
echo
self::$foo;
}
}
$first = new one;
$second = new two;

$second->tell_me(); // bar
$first->change_foo("restaurant");
$second->tell_me(); // restaurant
?>
up
7
bishop at php dot net
7 years ago
> Members declared protected can be accessed only within
> the class itself and by inherited classes. Members declared
> as private may only be accessed by the class that defines
> the member.

This is not strictly true. Code outside the object can get and set private and protected members:

<?php
class Sealed { private $value = 'foo'; }

$sealed = new Sealed;
var_dump($sealed); // private $value => string(3) "foo"

call_user_func(\Closure::bind(
function () use (
$sealed) { $sealed->value = 'BAZ'; },
null,
$sealed
));

var_dump($sealed); // private $value => string(3) "BAZ"

?>

The magic lay in \Closure::bind, which allows an anonymous function to bind to a particular class scope. The documentation on \Closure::bind says:

> If an object is given, the type of the object will be used
> instead. This determines the visibility of protected and
> private methods of the bound object.

So, effectively, we're adding a run-time setter to $sealed, then calling that setter. This can be elaborated to generic functions that can force set and force get object members:

<?php
function force_set($object, $property, $value) {
call_user_func(\Closure::bind(
function () use (
$object, $property, $value) {
$object->{$property} = $value;
},
null,
$object
));
}

function
force_get($object, $property) {
return
call_user_func(\Closure::bind(
function () use (
$object, $property) {
return
$object->{$property};
},
null,
$object
));
}

force_set($sealed, 'value', 'quux');
var_dump(force_get($sealed, 'value')); // 'quux'

?>

You should probably not rely on this ability for production quality code, but having this ability for debugging and testing is handy.
up
8
Joshua Watt
17 years ago
I couldn't find this documented anywhere, but you can access protected and private member varaibles in different instance of the same class, just as you would expect

i.e.

<?php
class A
{
protected
$prot;
private
$priv;

public function
__construct($a, $b)
{
$this->prot = $a;
$this->priv = $b;
}

public function
print_other(A $other)
{
echo
$other->prot;
echo
$other->priv;
}
}

class
B extends A
{
}

$a = new A("a_protected", "a_private");
$other_a = new A("other_a_protected", "other_a_private");

$b = new B("b_protected", "ba_private");

$other_a->print_other($a); //echoes a_protected and a_private
$other_a->print_other($b); //echoes b_protected and ba_private

$b->print_other($a); //echoes a_protected and a_private
?>
up
1
kostya at eltexsoft dot com
3 years ago
I see we can redeclare private properties into child class
<?php
class A{
private
int $private_prop = 4;
protected
int $protected_prop = 8;
}

class
B extends A{
private
int $private_prop = 7; // we can redeclare private property!!!
public function printAll() {
echo
$this->private_prop;
echo
$this->protected_prop;
}
}

$b = new B;
$b->printAll(); // show 78
}
?>
To Top