PHP 5.5 Classname Resolution - php

PHP 5.5 has implemented as a new feature a new way to retrieve the classname through the syntax ::class:
<?php
namespace Testing;
class Test{}
echo Test::class; // Testing\Test;
This works perfectly, alright? BUt what me and some other friends wanted to know is why this syntax also returns a classname when used alongside an undeclared class. E.g.:
<?php
echo UndeclaredClass::class; // UndeclaredClass
In several other cases an error is raised, but not here. Anyone know, with concrete basis if possible, why does this happen?
Does it have anything to Late Static Bindings or it's just a (temporary) limitation/bug of this brand new feature?

Finally an official answer... relatively speaking. It was presented to me by someone identified by requinix#php.net in a bu report i created today. The only exception is about how involved with PHP development this person is.
TL;DR
PHP doesn't need ot know the definition of a class to get its fully-qualified name. All the required informations are available in compile-time so it doesn't need to load it.
Director's Cut
Namespaces like the uses are resolved in compile-time, i.e., when the file is compiled before its execution. That's why there are strict requirements in order to use them.
Because of all of those requirements, when PHP encounters a class name it can immediately know its fully-qualified name. Thinking of it as a filesystem the namespace would be a directory for relative locations and the use would be symlinks.
The class name is either absolute ("\Testing\Test") or relative ("Test"), and if relative it could be a normal name. [more context required]
namespace Testing {
echo Test::class; // \Testing + Test = \Testing\Test
}
Or an alias:
use Testing\Test as AliasedTest;
echo AliasedTest::class; // AliasedTest + use = \Testing\Test
Without all of this autoloading wouldn't work!
::class is just a new tool to expose information PHP has always known.
This "extended answer" is pretty much the same of what I received as bug report. The reason of so much apparent copy & paste is because, originally, I built up this answer for another Stack Overflow Community

You can use get_class function get the class name with the namespace. It will be the good to use it.
Here is the code which you can try this code:
<?php
namespace Testing;
class Test{
public function abc()
{
echo "This is the ABC.";
}
}
namespace Testing1;
class Test{
public function abc()
{
echo "This is the ABC.";
}
}
// echo Test::class; // Testing\Test;
$test = new Test();
print_r(get_class($test));
// echo "<br>";
// echo UndeclaredClass::class;
?>

Related

Why use class aliases?

Why would we use the class_alias function? For example:
Class Test {
public function __construct(){
echo "Class initialized";
}
}
class_alias("Test", "AnotherName");
$instance = new AnotherName(); # equivalent to $instance = new Test();
According to the manual, "The aliased class is exactly the same as the original class."
What is this useful for?
Surprisingly, nobody has mentioned the obvious reason why one would do this: the use keyword can only be used in the outmost scope, and is processed at compile-time, so you can't use Some\Class based on some condition, nor can it be block-scoped:
namespace Foo;
if (!extension_loaded('gd'))
{
use Images\MagicImage as Image;
}
else
{
use Images\GdImage as Image;
}
class ImageRenderer
{
public function __construct(Image $img)
{}
}
This won't work: though the use statements are in the outmost scope, these imports are, as I said before, performed at compile-time, not run-time. As an upshot, this code behaves as though it was written like so:
namespace Foo;
use Images\GdImage as Image;
use Images\MagicImage as Image;
Which will produce an error (2 class with the same alias...)
class_alias however, being a function that is called at run-time, so it can be block scoped, and can be used for conditional imports:
namespace Foo;
if (!extension_loaded('gd'))
{
class_alias('Images\\MagicImage', 'Image');
}
else
{
class_alias('Images\\GdImage','Image');
}
class ImageRenderer
{
public function __construct(Image $img)
{}
}
Other than that, I suspect the main benefit of class_alias is that all code written, prior to PHP 5.3 (which introduced namespaces) allowed you to avoid having to write things like:
$foo = new My_Lib_With_Pseudo_Name_Spaces_Class();
Instead of having to refactor that entire code-base and create namespaces, It's just a lot easier to add a couple of:
class_alias('My_Lib_With_Pseudo_Name_Spaces_Class', 'MyClass');
To the top of the script, along with some //TODO: switch to Namespaces comments.
Another use case might be while actually transferring these classes to their namespaced counterparts: just change the class_alias calls once a class has been refactored, the other classes can remain intact.
When refactoring your code, chances are you're going to want to rethink a couple of things, so a use-case like aichingm suggested might not be too far fetched.
Last thing I can think of, but I haven't seen this yet, is when you want to test some code with a mock object, you might use class_alias to make everything run smoothly. However, if you have to do this, you might aswell consider the test a failure, because this is indicative of badly written code, IMO.
Incidently, just today I came across another use-case for class_alias. I was working on a way to implement a lib, distilled from a CLI tool for use in a MVC based web-app. Some of the classes depended on an instance of the invoked command to be passed, from which they got some other bits and bolts.
Instead of going through the trouble of refactoring, I decided to replace the:
use Application\Commands\SomeCommand as Invoker;
Statements with:
if (\PHP_SAPI === 'cli')
{
class_alias('\\Application\\Commands\\SomeCommand', 'Invoker');
}
else
{
class_alias('\\Web\\Models\\Services\\SomeService', 'Invoker');
}
and, after a quick :%s/SomeCommand/Invoker/g in vim, I was good to go (more or less)
Think of this the other way. There is a library and it contains a class called MySq, as the library moves on in development they think 'oh maybe we should just make one database class' so the rewrite the class and call it 'DatabaseConnection' it has the same functions as the MySql class but it can also handle Sql and MsSql. As time goes on and they release the new version 'PHPLib2'. The developer has now two options: 1, tell the users to change from MySql to DatabaseConnection or 2, the developer uses
class_alias("DatabaseConnection","MySql");
and just mark the class MySql as deprecated.
tltr;
To keep version compatibility!
It can be used for:
Changing strange class names of ext. scripts/sources (without the need to rewrite the code)
Making class names shorter (the class alias can be used application wide)
Moving classes to another namespace (#tlenss)

Is there any way to make PHP load classes verbosely, indicating the full path to the .php file?

I would like to make PHP print out debugging information, including the full path to the .php file, to standard error whenever it loads a class: e.g.
Loaded MyClass from /path/to/my/class/MyClass.php
is there any way to do this without knowing in advance where the source files are?
[edited to clarify that I really care about the full path to the .php file, and that I don't know in advance where the source files are]
You can use __autoload($class).
<?php
function __autoload($class_name)
{
echo "Loading: $class_name";
include $class_name . '.php';
}
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
To do this, you just skip your explicit imports, like "include x.php" or "require_once x.php", and the autoloader finds it for you.
Since PHP does not have static constructors, you cannot automagically do something when a class is loaded. Your best bet is probably to print the message after the class definition (or use __autoload as Josh instructed, but that might require some reworking on your end).
class Foo
{
/* stuff */
}
echo "Class Foo loaded from " . __FILE__ . "\n";
EDIT Sorry to say, but PHP provides absolutely no hook to when a class is loaded or first instantiated, even in the dirtiest corners of its weird extensions. You will not be able to get away without either editing the classes' source files (and use my solution) or organize them in a conventional hierarchy to put them in (and use Josh's solution).
There is feature request #48546 that asks for a way to set a callback to when a file will be included but it's not going anywhere. Otherwise, people seem content with __autoload.
At best, you may call get_declared_classes at any time and see what's already been loaded.
You could put a message into the constructor function for each class that will output some sort of debugging message like this (though I am going to have to see if it is possible to find the exact document that the source is in and add it in if I find it) but this should give you an idea:
<?php
class something()
{
public function __construct()
{
echo "New instance of class:something is being made.";
echo "Class is loaded from ".realpath(__FILE__);
}
}
$var=new something();
?>
Output:
New instance of class:something is being made.
Class is loaded from /var/www/someFolder/incs/myclasses.php
Edit: Above change will echo out the message when you create a new object of the class like $var= new something(); it won't kick in at any point prior to that though.

PHP Namespaces & Referencing Classes not contained within Namespaces

I have a simple question, which should hopefully have a quick answer. The code I have written makes heavy use of namespaces (I use fully qualified names everywhere); however, a piece of code (a calendar / date picker control, not written by myself) needs to be included. When I attempt to create the control, it assumes the current namespace (GUI), resulting in this error: "PHP Fatal error: Class 'GUI\tc_calendar' not found in C:\inetpub\wwwroot\Calico\calico_classes_v2.php on line 1852". Now, the calendar control itself (and it's underlying class file) does not make use of namespaces, and I am a little worried about attempting to modify it (an earlier attempt did not go well).
How can I import / include a control, that is not contained within a namespace, into the rest of my code, that does? Does PHP have something like "Import class calendar from file AS \Calendar\Calendar"?
Edit:
For additional information: I have a class, called "tc_calendar", contained in a file called "tc_calendar.php". It is not part of any namespace.
In a separate file, I have several classes (Bitmap, CompositeCalendar, EventEditor, TimeExtractor), all contained within their appropriate namespaces (GUI, Data, Extract, etc.).
In one of those classes, CompositeCalendar, contained within the GUI namespace, I am trying to create an instance of a tc_calendar. However, PHP is throwing an error (above). tc_calendar is not a part of any namspace (and definitely not a part of the GUI namespace, which PHP is just assuming, because it can't seem to find it), and I need help creating an instance of it.
tldr; $newcontrol = new tc_calendar() doesn't work; PHP tries to guess the namespace for it (because one isn't specified, because tc_calendar isn't a part of any namespace), comes up with \GUI\tc_calendar (obviously wrong). How do I access a class, not contained within a namespace, from inside a namespace?
Do you mean something like this:
namespace GUI;
use \tc_calendar as Calendar;
$myCalendar = new Calendar();
The as Calendar is optional. You could aswell keep it with the original name tc_calendar if you ommit the as Calendar.
Update
To put it in shape of the comment:
namespace {
class tc_calendar {}
}
namespace GUI {
use \tc_calendar;
class CompositeCalendar {
private function blah() {
$control = new tc_calendar();
$control->stuff();
}
}
}
I wouldn't copy paste external libraries into he same file though. It bad practise. It is better to keep them in another file and then include them and have the following:
namespace GUI;
use \tc_calendar;
require_once 'tc_calendar.php';
class CompositeCalendar {
private function blah() {
$control = new tc_calendar();
$control->stuff();
}
}
Or combine my 3 snippets to have it any other form you like.
Also I would suggest to extend the calendar if you are just building calendar class based on the the tc_calendar:
namespace GUI;
use \tc_calendar;
require_once 'tc_calendar.php';
class CompositeCalendar extends tc_calendar {
private function blah() {
$this->stuff();
}
}
Any class not in a namespace is automatically in the global namespace.
To refer to anything in the global namespace from anywhere, use a single preceding \:
new \tc_calendar;

Is this a good way to use Namespaces in PHP

I have studied the use of Namespaces in PHP a while back but recently looking at a project that used the use keyword and then accessed the namespaced object as if they were normal without namespace.
My question is, is the code below correct, it hs a file index.php and uses the namespace MyLibrary\Base it then uses use to bring in \MyLibrary\Registry \MyLibrary\User and \MyLibrary\Request
It then can access any of these object without putting there namespace in front of them, so the actual code below the use section looks like a normal pre-namespace php file.
I am asking if this is how you use namespaces? Or am I missing something?
File: index.php
<?php
namespace MyLibrary\Base;
use \MyLibrary\Registry;
use \MyLibrary\User;
use \MyLibrary\Request;
class Base
{
public $registry;
function __construct($registry)
{
$this->registry = $registry;
$this->user = New User;
$this->request = new Request;
# code...
}
}
?>
File: registry.class.php
<?php
namespace MyLibrary\Registry;
class Registry
{
public $user;
function __construct($user)
{
$this->user = $user;
# code...
}
}
?>
Yes. The use-statement imports the class- or namespace-name into the current scope. To write everything that short is the reason, why the PHP-devs implemented namespaces ;)
namespace MyFirstNamespace {
class Foo {}
}
namespace MySecondNamespace {
use \MyFirstNamespace\Foo as Bar;
$foo = new Bar;
}
a) it make everything more readable, because its much shorter, than Vendor_Package_Foo_Bar_XyzClass and b) you can exchange the classes to use very fast.
# use \MyFirstNamespace\Foo as Bar; // I don't like Foo anymore
use \MyFirstNamespace\SimilarToFoo as Bar;
Namespacing has a lot of advantages to it.
The first on is you can reuse method names and even class names if it makes sense provided they exist within a different namespace. Example:
namespace \myNamespace\data\postgres;
class DataBase extends \PDO
{
}
namespace \myNamespace\data\mysql;
class DataBase extends \PDO
{
}
You could even reuse names that are normally reserved for PHP functions
namespace \myNamespace\dir;
function makedir ()
{
if (// some condition is true)
{
\makedir ();
}
}
All of this is intended to make it easier to use code from different sources together without having to worry about naming conflicts. Provided programmers are courteous enough to observe a few simple rules then the chances of name conflicts are hugely reduced. Actually, pretty much the only rule you need to concern yourself with to avoid naming conflicts is to make the first level of your namespace your own. For example, use your company name, or some other way of identifying you as a unique vendor as the first level of your namespace and everything should be good.
For example I use \gordian as the root namespace in all the code I write, as I can then call my classes under that namespace anything I like without worrying about them colliding with someone who chose a different root namespace.
So what's wrong with PEAR conventions, you might ask? A lot of projects follow them, including the popular Zend framework.
The answer is names become very unwieldy very quickly. For instance, Zend, as it follows the PEAR convention, uses a sort of pseudo-namespacing. All the classes in the collection start with Zend_ and with each level of class hierachy add a further part to the name.
Ze
As a result, you end up with class names like Zend_Db_Adaptor_Abstract and Zend_Dojo_Form_Decorator_TabContainer.
Should Zend update their framework to use namespaces (which I'm told is happening with Zend Framework 2.0) then they'd be replaced with \Zend\Db\Adaptor\Abstract and \Zend\Dojo\Form\Decorator\TabContainer instead. So what, you might ask? The answer is that you can alias them to much shorter names with the Use keyword, as you've already seen. This means you don't have to keep writing the full class name out, but only as far as to what you've aliased.
use \Zend\Dojo\Forn\Decorator as Dec;
$a = new Dec\TabContainer; // Not easy to do without namespaces!
Further more, if you're already in a given namespace, then you don't even have to use the use keyword to access other items within the same namespace by a short name, as it happens automatically for you in that case. For framework writers this is a huge timesaver.
For example, you might see something like the followign in Zend Framework 2 (as I'm not working on it in any way, this is purely an example and not from the actual ZF2 source).
namespace \Zend\Db\Adaptor;
class Postgres extends Abstract // We don't need to use \Zend\Db\Adaptor\Abstract here because it's in the same namespace already anyway
{
}
There are other benefits too, such as it makes autoloaders ridiculously simple to make (provided your namespace structure maps exactly onto your filesystem directory structure).
Namespaces can seem like one of those features that aren't really very important, or don't even seem to make any sense, but after using them for a little while their usefulness will suddenly become very obvious.

Is it possible to have two classes with the same name if they're in different folders?

I was wondering if there is anything wrong with having two classes with the same name in PHP if they're in different sub folders, other than the obvious "human factor" of editing the wrong file by mistake?
I have looked for other posts relating to this, here and elsewhere on the web, but I didn't find any that could answer this specific question. I did however find this Autoload classes from different folders very helpful though, and in fact it solved one of my other questions.
This is possible to have classes with same name even in same folder.
But Make sure you have loaded only one class in the PHP script at a time.
They can not be loaded in the same script at same time.
PHP does not know if you have created two classes with same name but the fact is PHP will not load them in same script. You can use one class at a time.
You can also look at namespaces in php.
That's where namespaces come in.
http://www.php.net/manual/en/language.namespaces.rationale.php
http://www.php.net/manual/en/language.namespaces.basics.php
This allows you to differentiate between the two classes of the same name.
Of course you can create the files in the same folder or different folders with the same class names, but you can only use one implementation in one file.
If you really need to give the two classes the same name and must use them in one file, a solution might be namespaces... http://www.php.net/manual/en/language.namespaces.rationale.php
This is possible to have classes with the same name even in the same folder. Here is the sample of code.
file name: namespace.php
<?php
namespace MyProject {
class Connection {
public function __construct(){
echo 'My Project class call';
}
}
function connect() {
echo 'My Project connect function.';
}
}
namespace AnotherProject {
class Connection {
public function __construct(){
echo 'Another Project class call';
}
}
function connect() {
echo 'Another Project connect function.';
}
}
?>
Another file, where we use this namespace.
file name: myapp.php
<?php
require 'namespace.php';
//create a class object
$obj = new MyProject\Connection;
//calling a function
MyProject\connect();
//calling a another function
AnotherProject\connect();
?>
You can use PHP Aliasing:
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
use App\Company;
use App\Domain\Company\Models\Company as Tcompany;
https://www.php.net/manual/en/language.namespaces.importing.php
In fact you can, but think also about the overloading, and about the interfaces...
I believe you will have a conflict when you'll instantiate these classes. Actually I've never tested it, but PHP does not behave like Java, where you can put classes with the same name in different packages, and specify the package to differentiate them upon instantiation...

Categories