Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

::namespace #178

Open
thekid opened this issue Mar 2, 2024 · 3 comments
Open

::namespace #178

thekid opened this issue Mar 2, 2024 · 3 comments
Labels

Comments

@thekid
Copy link
Member

thekid commented Mar 2, 2024

How about using ::namespace for namespace resolution just like ::class?

Example:

<?php namespace de\thekid\dialog;

// Currently, using the dotted syntax
$api= new RestApi(new ResourcesIn('de.thekid.dialog.api'));

// The PHP way
$api= new RestApi(new ResourcesIn(__NAMESPACE__.'\\api'));

// Suggestion
$api= new RestApi(new ResourcesIn(api::namespace));

This would simply be emitted as ::class, which already does everything we want; however, using api::class in this place doesn't convey the intent correctly!

@thekid thekid added the question label Mar 2, 2024
@thekid
Copy link
Member Author

thekid commented Mar 2, 2024

This would break class constants named namespace:

$ xp -w 'class T { const namespace = "ns"; } return T::namespace'
ns

@thekid
Copy link
Member Author

thekid commented Mar 2, 2024

Implementation:

diff --git a/src/main/php/lang/ast/emit/PHP.class.php b/src/main/php/lang/ast/emit/PHP.class.php
index fe08e57..6342402 100755
--- a/src/main/php/lang/ast/emit/PHP.class.php
+++ b/src/main/php/lang/ast/emit/PHP.class.php
@@ -1086,17 +1086,17 @@ abstract class PHP extends Emitter {
       $result->out->write("{$scope->type}::");
     }
 
-    // Rewrite T::member to T::$member for XP enums
-    if (
-      $scope->member instanceof Literal &&
-      is_string($scope->type) &&
-      'class' !== $scope->member->expression &&
-      $result->codegen->lookup($scope->type)->rewriteEnumCase($scope->member->expression)
-    ) {
-      $result->out->write('$'.$scope->member->expression);
-    } else {
-      $this->emitOne($result, $scope->member);
+    // Rewrite ::class and ::namespace to namespace resolution; T::member to T::$member for XP enums
+    if ($scope->member instanceof Literal) {
+      if ('namespace' === $scope->member->expression || 'class' === $scope->member->expression) {
+        $result->out->write('class');
+        return;
+      } else if (is_string($scope->type) && $result->codegen->lookup($scope->type)->rewriteEnumCase($scope->member->expression)) {
+        $result->out->write('$'.$scope->member->expression);
+        return;
+      }
     }
+    $this->emitOne($result, $scope->member);
   }
 
   protected function emitInstance($result, $instance) {
diff --git a/src/test/php/lang/ast/unittest/emit/NamespacesTest.class.php b/src/test/php/lang/ast/unittest/emit/NamespacesTest.class.php
index 7fe7dcc..8db6133 100755
--- a/src/test/php/lang/ast/unittest/emit/NamespacesTest.class.php
+++ b/src/test/php/lang/ast/unittest/emit/NamespacesTest.class.php
@@ -84,4 +84,14 @@ class NamespacesTest extends EmittingTest {
     }');
     Assert::equals(new Date('1977-12-14'), $r);
   }
+
+  #[Test]
+  public function namespace_resolution() {
+    $r= $this->run('namespace util; class %T {
+      public function run() {
+        return cmd::namespace;
+      }
+    }');
+    Assert::equals('util\\cmd', $r);
+  }
 }
\ No newline at end of file

@thekid
Copy link
Member Author

thekid commented Mar 2, 2024

Someone suggested this to return the namespace name of a class in https://externals.io/message/113269:

use util\cmd\Console;

$ns= Console::namespace; // "util\cmd"

If we introduced this feature as described above, the above would yield "util\cmd\Console". Currently, it yields an error for an undefined class constant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant