php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #68593 Namespace not resolved at runtime like with static::
Submitted: 2014-12-11 22:07 UTC Modified: 2014-12-12 09:05 UTC
From: llmll at gmx dot de Assigned:
Status: Duplicate Package: Scripting Engine problem
PHP Version: Irrelevant OS: any
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: llmll at gmx dot de
New email:
PHP Version: OS:

 

 [2014-12-11 22:07 UTC] llmll at gmx dot de
Description:
------------
The problem is equivalent to self:: and static:: but on the namespace realm. self:: resolves to the defining class while static:: resolves to the current instance class.

Take a second to understand the test script and expected result.

Reopening #68485 as current behaviour deviates clearly from the documentation, which the original maintainer failed to grasp.

Test script:
---------------
// original namespace
namespace Alpha;

class Helper {
  public static $Value = "ALPHA";
}

class Caller {
  public static function Write() {
    // call to relative neighbour class Helper, resolves to \Alpha\Helper::
    echo Helper::$Value;
  }
}

// now both classes are inherited into another namespace
namespace Beta;

class Helper extends \Alpha\Helper {
  public static $Value = 'BETA';
}	

class Caller extends \Alpha\Caller {}	

\Beta\Caller::Write();

Expected result:
----------------
This should print "BETA". PHP namespace resolution documentation states: 

Inside namespace (say A\B), calls to unqualified or qualified class names (not fully qualified class names) are resolved at >>> RUN-TIME <<<. This only makes sense, if at runtime the current class scope and the current namespace are taken into account to resolve the class. Otherwise resolving at runtime or at compiletime makes no difference.

Actual result:
--------------
The call to Helper::$Value is resolved at complie-time which is breaking inheritance logic. 

Helper:: should resolve to the instance namespace, because it is called without a namespace qualifier, meaning we want the relative class to the current namespace. Otherwise we could have written the fully-qualified \Alpha\Helper to fix the used Helper class, even in child classes.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-12-11 22:37 UTC] requinix@php.net
-Status: Open +Status: Duplicate
 [2014-12-11 22:37 UTC] requinix@php.net
No. We argued about this in your other bug report.

The. Current. Behavior. Is. Correct.

If you feel the documentation needs clarification to explain the resolution process better - that unqualified class names in code are always resolved according to the namespace WHERE THE CODE IS ACTUALLY WRITTEN - then submit a patch against it:
1. Go to the relevant page and click the Edit link in the top-right corner
2. Log in (or stay anonymous)
3. Make your change to the documentation's source XML
4. Save and submit the change for review
 [2014-12-11 23:09 UTC] llmll at gmx dot de
Hi requinix@php.net,

stay calm. A bug is no reason to shout at people. 

Coding is not a matter of feeling but of precision. There is runtime and there is write-time and there is a big difference which you play down. Why not ponder on the problem, and then admit and correct it? Everybody will benefit from clarification.

(I remember doing the same convincing for the necessity of static::, when years ago PHP lacked late static binding and only had the nowadays completely useless self:: operator. In time, people appreciated the late static binding :-))

Now comes the difficult part. In our project we really need to address the current namespace at runtime, as it offered in PHP's documentation. I see no way of explaining the why and how to you, considering your prior reasoning.

Just for reference for others, here is how we compensate for it:

public static function typeNameOfPeer($aClass) {
  return static::moduleName() . '\\' . $aClass;
}

moduleName() returns the current class' namespace. So if we want to call a class dynamically in the current namespace, we need to write:

$class = static::typeNameOfPeer('RelativeNameOfClass');
$class::callAMethod();

I was hoping to get in touch with another maintainer. Please don't be offended or take it personally when I request the bug to be transferred to a colleague who knows PHP namespaces and inheritance mechanics. Thanks.
 [2014-12-11 23:21 UTC] nikic@php.net
The current behavior is intended and correct. Class names, including unqualified class names, are always resolved at compile time.

The confusion probably comes from the incorrect or at least misleading wording on http://php.net/manual/en/language.namespaces.rules.php. That document conflates name resolution (which is always compile-time for class names) and autoloading (which is the runtime operation). It should probably be clarified (don't know why it even mentions autoloading there - that's not related to name resolution).
 [2014-12-11 23:34 UTC] llmll at gmx dot de
How then would you call a namespace resolution that delivers that expected result? 

At compile time both \Beta\Caller and \Beta\Helper exist. The method in Caller::Write() uses the unqualified class identifier Helper:: to access it's peer  class. I think the problem is not between run time and compile time but executing class and defining class.

Need proof? If you copy&paste the Write() method into \Beta\Helper, everything works as expected. But thats the whole point in OOP to _not_ copy and paste code, isn't it?

Maybe we can convert this matter to a feature request?
 [2014-12-12 00:06 UTC] requinix@php.net
Sorry about the yelling, llmll, it's just that I spent a lot of time trying to explain to you in the other ticket why the "current namespace" is not relative to calling code, then I see you come here and make another ticket on the exact same issue. Makes me think all this is a waste of my time.
And since you've stated that you're deliberately refusing to listen to me, maybe it was.
 [2014-12-12 09:05 UTC] llmll at gmx dot de
Thanks for your explanation. I appreciate your moderation and tend to think every reported bug should get the same appreciation. What I mean is, that I spent hours with the problem, analyzing, writing code samples, reporting. Getting closed down by a moderator who seemd to got stuck into the mantra "it is never a bug", is quite frustrating. See, how I could understand your handling of bugs as waste of time? Believe me, I only report the serious bugs I come across.

So why waste more time? Maybe PHP does what it should, at least it does what it was coded to be done. But I think the current class resolution is a design flaw. I think the documentation was written by someone who understood namespace contexts and interpolated the behaviour correctly on the paper, but didn't verify it with code. That is what I have done. 

The example with the filesystem is very plausible. Imagine a project directory:
/alpha
  .
  ..
  caller.php <- relative symlink to ./helper.php
  helper.php

Now you copy the directory over to another project, lets say
/beta
  .
  ..
  caller.php <- still a relative symlink to ./helper.php
  helper.php

Which helper.php file would you expect caller.php to resolve to? /alpha/helper.php or /beta/helper.php? 

Give me sound reason to _always_ expect it to be /alpha/helper.php and we mark this bug as resolved.

Therefore Im reluctant to open a feature request as this would devalue the problem to a nice-to-have thing, while I think it is a must-have. BUT, if this is the best I can get - I'll take it.
 [2014-12-18 14:29 UTC] llmll at gmx dot de
Could you please remove the "duplicate" status? I doubt, any developer will look at it otherwise.
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Sat Aug 15 17:01:26 2020 UTC