BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles PHP 7 — Classes and Interfaces Improvements

PHP 7 — Classes and Interfaces Improvements

Leia em Português

This item in japanese

Key Takeaways

  •  PHP 7 added anonymous classes for one-off objects; examples being a value object, and an object that implements an interface for dependency injection. 
  • Anonymous classes are designed for single-use and don't require full class definition. 
  • Anonymous classes are just like full-fledged classes and may extend other classes, implement interfaces, define constructor args, etc.
  • PHP 7 introduced the IntlChar class to access information about Unicode characters.
  • PHP 7 deprecated some features such as the PHP 4-style constructors.

PHP 7.x brings several improvements and new features that touch all aspects of the language, including better support for object oriented programming, extensions to classes and interfaces, improvements to the type system, error handling, and more. In this series of articles, we discuss new features across the various PHP 7.x versions.

 

In this article series, we are exploring new features in PHP 7. In the first article, we prepared the environment and introduced PHP 7, then discussed its new features related to object-oriented programming.  In this article, we shall discuss improvements made to PHP classes and interfaces.

Anonymous Classes

Often, short-term use, throwaway objects can be used in place of  full-fledged class instances.

PHP 7.0 added support for anonymous classes that are convenient to instantiate even for single use. Anonymous classes are just like full-fledged classes in that they may extend other classes, implement interfaces, define constructor args, etc.

As an example, we shall create an anonymous class to handle log messages for a server log. Create an anonymous.php script and define an interface LogMsg containing a function setMsg(string $msg) which allows you to set a log message.  Additionally, create a class ServerLog with getter/setter methods getLogMsg(): LogMsg and  setLogMsg(LogMsg $logMsg) to set a log message.

<?php
interface LogMsg {
	public function setMsg(string $msg);
}
class ServerLog {
	private $logMsg;
	public function getLogMsg(): LogMsg {
     	return $this->logMsg;
	}
	public function setLogMsg(LogMsg $logMsg) {
         $this->logMsg = $logMsg;
	}
}
$serverLog = new ServerLog;
$serverLog->setLogMsg(new class implements LogMsg {
	public function setMsg(string $msg) {
    	echo $msg;
	}
});
var_dump($serverLog->getLogMsg());
?>

 

Create an instance of the ServerLog class and invoke the setLogMsg(LogMsg $logMsg) function with the arg supplied as an anonymous class. 

$serverLog = new ServerLog;
$serverLog->setLogMsg(new class implements LogMsg {
	public function setMsg(string $msg) {
    	echo $msg;
	}
});

If you run the script,  var_dump will output a reference to the anonymous class object we passed into SetLogMsg.

object(class@anonymous)#2 (0) { }

If we had not used an anonymous class in the preceding example, we would have needed to provide a full class definition implementing the LogMsg interface. The same example without using anonymous classes looks  as follows.

<?php
interface LogMsg {
	public function setMsg(string $msg);
}
class ServerLogMsg implements LogMsg {
	public function setMsg(string $msg) {
    	echo $msg;
	}
}
class ServerLog {
	private $logMsg;
	public function getLogMsg(): LogMsg {
     	return $this->logMsg;
	}
	public function setLogMsg(LogMsg $logMsg) {
         $this->logMsg = $logMsg;
	}
}
$serverLog = new ServerLog;
$serverLog->setLogMsg(new ServerLogMsg());
var_dump($serverLog->getLogMsg());
?>

All objects instantiated from the same anonymous class declaration are instances of that very class and identical to each other. The identical comparison is performed with the === operator and it implies the objects being compared are equal and of the same type. Identity comparison using === could appear confusing at first. Let’s start with the equality operator ==. Two object instances are equal (compared with == operator) if they have the same attributes and values and are instances of the same class. With the identity comparison operator (===) two objects are identical if and only if they refer to the same instance of the same class.

To make sense of this, create a script anonymous-class-objects.php and define a function that returns an anonymous class object.  Now, using the get_class function, obtain the name of the class of two different anonymous objects instantiated by calling  the function twice and compare those names. As we show below, the two names will be equal.

<?php
function a_class()
{
	return new class {};
}
 
if(get_class(a_class())===get_class(a_class())){
echo "Objects are instances of same class ".get_class(a_class());
}else{
echo "Objects are instances of different classes";
}
echo "</br>";
var_dump(get_class(a_class()));
echo "</br>";
var_dump(get_class(a_class()));
?>

Running the script anonymous-class-objects.php will produce an output message indicating that the objects are instances of the same class, whose name starts with class@anonymous. The same instance class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031 is returned, which indicates two identical classes. The anonymous class name is assigned by the PHP engine and is implementation dependent.

Objects are instances of same class class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031
string(98) "class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031"
string(98) "class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031"

An anonymous class may also extend another class using extends.

To demonstrate this, create a script anonymous-extend-class.php and define a class LogMsg with a field $msg and get/set functions for that field. Now, create a class ServerLog with functions getLogMsg(): LogMsg and setLogMsg(LogMsg $logMsg). Finally, create an instance of the ServerLog class and invoke the setLogMsg(LogMsg $logMsg) function with the LogMsg arg supplied as an anonymous class that extends another class LogMsg:

$serverLog = new ServerLog;
$serverLog->setLogMsg(new class extends LogMsg {
	public function setMsg(string $msg) {
        $this->msg = $msg;
	}
});

The anonymous-extend-class.php script is listed:

<?php
class LogMsg {
private $msg;
	public function getMsg() {
        return  $msg;
	}
}
class ServerLog {
	private $logMsg;
	public function getLogMsg(): LogMsg {
     	return $this->logMsg;
	}
	public function setLogMsg(LogMsg $logMsg) {
         $this->logMsg = $logMsg;
	}
}
$serverLog = new ServerLog;
$serverLog->setLogMsg(new class extends LogMsg {
	public function setMsg(string $msg) {
        $this->msg = $msg;
	}
});
var_dump($serverLog->getLogMsg());
?>

Run the script and check its output. You will see the msg field of type LogMsg is set to NULL.

object(class@anonymous)#2 (1) { ["msg":"LogMsg":private]=> NULL }

As you may expect, an argument may be passed to an anonymous class constructor.

To demonstrate this, create a script anonymous-extend-class-add-constructor.php and define classes LogMsg and ServerLog as in the preceding example.  The only difference is that an arg is passed to the anonymous class  constructor:

$serverLog->setLogMsg(new class('Log Message') extends LogMsg {
	public function __construct($msg)
	{
        $this->msg = $msg;
	}
…
}

The anonymous-extend-class-add-constructor.php script is as follows.

<?php
class LogMsg {
private $msg;
	public function getMsg() {
        return  $msg;
	}
}
class ServerLog {
	private $logMsg;
	public function getLogMsg(): LogMsg {
     	return $this->logMsg;
	}
	public function setLogMsg(LogMsg $logMsg) {
         $this->logMsg = $logMsg;
	}
}
$serverLog = new ServerLog;
$serverLog->setLogMsg(new class('Log Message') extends LogMsg {
	public function __construct($msg)
	{
        $this->msg = $msg;
	}
	public function setMsg(string $msg) {
        $this->msg = $msg;
	}
});
var_dump($serverLog->getLogMsg());
?>

Run the script and verify the log message passed to the anonymous class constructor and returned by getLogMsg() is printed out.

object(class@anonymous)#2 (2) { ["msg":"LogMsg":private]=> NULL ["msg"]=> string(11) "Log Message" }

An anonymous class may be nested within another class but it can’t use the protected or private functions or properties from the outer class. To use the private properties of the outer class in the anonymous inner class, pass the properties as args to the anonymous class constructor as shown in the preceding example.

To demonstrate this, create a script inner-class-private.php and define an outer class Outer with a private property. Add a function inner() which returns an anonymous class object. The private property from the Outer class is passed to the anonymous class constructor and set as a private property of the anonymous class. Now, using a function declared in the anonymous class, you can return the private property value passed from the Outer class to the anonymous  inner class:

return new class($this->a) extends Outer {
             private $a;
            public function __construct($a)
        	{
                $this->a = $a;
        	}
            public function getFromOuter()
        	{
                echo $this->a;
        	}
    	};

To print out the private property value passed from Outer to the anonymous  inner class, create an instance of the Outer class and invoke the function inner(), which creates the anonymous class, and invoke the anonymous class function that returns the private property’s value:

echo (new Outer)->inner()->getFromOuter();

The inner-class-private.php script is listed below.

<?php
class Outer
{
	private $a = 1;
	public function inner()
	{
    	  return new class($this->a) {
             private $a;
            public function __construct($a)
        	{
                $this->a = $a;
        	}
        	            public function getFromOuter()
        	{
          	  echo $this->a;	
        	}
    	  };
	}
}
echo (new Outer)->inner()->getFromOuter();
?>

Run the script and check the private property value (1) passed from Outer to the inner class is printed out in the browser.

Next, we shall demonstrate how a protected function from an Outer class may be invoked in an anonymous class. To invoke one of the protected functions defined in the outer class, the anonymous inner class would need to extend the former.

To demonstrate this, create a script inner-class-protected.php and define an outer class called Outer with a protected field and a protected function. Now, define another function which creates an anonymous class that extends the Outer class and make this anonymous class define a function that invokes the outer class protected function we defined in the first place. As the anonymous class extends the Outer class, it inherits its protected fields and functions, which implies that the protected function and field  may be accessed using this. The anonymous class function is invoked as before by first creating an instance of the Outer class:

echo (new Outer)->inner()->getFromOuter();

The inner-class-protected.php script is listed.

<?php
class Outer
{
	protected $a = 1;
	protected function getValue()
	{
    	return 2;
	}
	public function inner()
	{
    	return new class extends Outer {
            public function getFromOuter()
        	{
                echo $this->a;
                echo "<br/>";
                echo $this->getValue();
        	}
    	};
	}
}
echo (new Outer)->inner()->getFromOuter();
?>

Run the script and check the value returned by the Outer class protected field and Outer class functions that are printed out.

1

2

We used two examples; one to demonstrate how to invoke private field/s from the outer class, and the other to invoke protected fields and functions from the outer class in a nested anonymous class. The two examples may be combined by making the anonymous nested class extend the outer class to inherit the outer class’ protected fields and functions, as well as to pass the outer class’ private fields to the anonymous class’ constructor as follows.

return new class($this->prop) extends Outer {
…
}

To demonstrate this, create a script inner-class.php.

<?php
class Outer
{
	private $prop = 1;
	protected $prop2 = 2;
	protected function func1()
	{
    	return 3;
	}
	public function func2()
	{
    	return new class($this->prop) extends Outer {
            private $prop3;
            public function __construct($prop)
        	{
                $this->prop3 = $prop;
        	}
            public function func3()
        	{
                return $this->prop2 + $this->prop3 + $this->func1();
        	}
    	};
	}
}
echo (new Outer)->func2()->func3();
?>

Running the script will output the value 6 as obtained from invoking the outer class’ fields and functions.

New IntlChar Class for Unicode Characters

PHP 7.0 has introduced a new class called IntlChar with several utility methods to access information about Unicode characters. Note  that the Intl extension needs to be installed to use the IntlChar class, which is possible by uncommenting the following line in your php.ini configuration file:

extension=intl

Some of the methods in the IntlChar class are described in Table 2.

Table 2.  IntlChar Class Methods

Method

Description

IntlChar::charFromName

Returns the code point value  of a Unicode character by name

IntlChar::charName

 Returns the name of a unicode character

IntlChar::charType

Returns the general category value for unicode code point. For example for the title case letter category is IntlChar::CHAR_CATEGORY_TITLECASE_LETTER. For decimal number category is IntlChar::CHAR_CATEGORY_DECIMAL_DIGIT_NUMBER. If character is not in any of the defined  categories the category is IntlChar::CHAR_CATEGORY_UNASSIGNED.

IntlChar::chr

Returns Unicode character by code point value

IntlChar::getNumericValue

Returns the numeric value for a unicode code point.

IntlChar::isdefined

Returns a boolean to indicate if a character is defined

 

Create now a script Intlchar.php to test out some of these methods. In the following example, we are going to output the unicode version with constant IntlChar::UNICODE_VERSION;  to find what the unicode code point for LATIN CAPITAL LETTER B is; and to  find if \u{00C6} is defined. The script Intlchar.php is listed below.

<?php
printf('Unicode Version : ');
echo "<br/>";
echo IntlChar::UNICODE_VERSION;
echo "<br/>";
echo IntlChar::charFromName("LATIN CAPITAL LETTER B");
echo "<br/>";
var_dump(IntlChar::isdefined("\u{00C6}"));
 
?>

Running the script will produce the following output:

Unicode Version :
12.1
66
bool(true)

Deprecated Features

PHP 7 also deprecates a number of features.

Among deprecated features in PHP 7.0.x is PHP 4, “old”-style constructors i.e., constructor methods with the same name as the class.

As an example, create a script constructor.php and copy the following listing to it.

<?php
class Catalog {
	function Catalog() {
	}
}
?>

The script declares a class Catalog with a method using the same name Catalog. Run the script and the following message gets output.

Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; Catalog has a deprecated constructor

Additionally, static calls to non-static methods are deprecated in PHP 7.0.0.  To demonstrate this, create a script static.php and copy the following listing to it which declares a class with a non-static function getTitle() now try to make a static call to that function:

<?php
class Catalog {
	function getTitle() {
	}
}
Catalog::getTitle();
?>

Running this script will output the following message:.

Deprecated: Non-static method Catalog::getTitle() should not be called statically

In PHP 7.1.x the mcrypt extension has been deprecated.  Deprecated features in PHP 7.2 include unquoted strings, the __autoload() method, the create_function(), cast to unset, using parse_str() without a second argument, gmp_random() function, each() function, assert() with a string argument, and the read_exif_data() function. Deprecated features in PHP 7.3 include case-insensitive constants, and declaring assert() in a namespace.

As an example of deprecation of case insensitive constants, run the following script in which the define() function is invoked with case_insensitive parameter set to true:

<?php
define('CONST_1', 10, true); 
var_dump(CONST_1); 
var_dump(const_1);
?>

This will give you the following messages:.

Deprecated: define(): Declaration of case-insensitive constants is deprecated   on line 2

int(10)

Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "CONST_1"   on line 4

Summary

In this second article in the series on PHP 7 we explored the new features for classes and interfaces. The most notable new feature is support for anonymous classes. Unicode gets also a boost with a new class IntlChar which can be used to get information about Unicode characters.

In the next article in the series we shall explore the new features in PHP’s type system.

About the Author

Deepak Vohra is a Sun Certified Java Programmer and Sun Certified Web Component Developer. Deepak has published Java and Java EE related technical articles in  WebLogic Developer's Journal, XML Journal, ONJava, java.net, IBM developerWorks, Java Developer’s Journal, Oracle Magazine, and devx. Deepak has published five books on Docker and is a Docker Mentor. Deepak has also published several articles on PHP and a book Ruby on Rails for PHP and Java Developers. 

 

PHP 7.x brings several improvements and new features that touch all aspects of the language, including better support for object oriented programming, extensions to classes and interfaces, improvements to the type system, error handling, and more. In this series of articles, we discuss new features across the various PHP 7.x versions.

 

 

 

Rate this Article

Adoption
Style

BT