Skip to content

Type visibility #186

@thekid

Description

@thekid

Suggestion

Support visibility modifiers on types:

  • public - type can be used anywhere
  • protected - type can only be used inside the same namespace and sub-namespaces
  • private - type can only be used inside the same namespace

See https://externals.io/message/127341

POC (syntactical support)

Diff for xp-framework/ast:

diff --git a/src/main/php/lang/ast/syntax/PHP.class.php b/src/main/php/lang/ast/syntax/PHP.class.php
index 80186a6..9fcbd16 100755
--- a/src/main/php/lang/ast/syntax/PHP.class.php
+++ b/src/main/php/lang/ast/syntax/PHP.class.php
@@ -863,6 +863,24 @@ class PHP extends Language {
       return $type;
     });
 
+    $this->stmt('public', function($parse, $token) {
+      $type= $this->statement($parse);
+      $type->modifiers[]= 'public';
+      return $type;
+    });
+
+    $this->stmt('protected', function($parse, $token) {
+      $type= $this->statement($parse);
+      $type->modifiers[]= 'protected';
+      return $type;
+    });
+
+    $this->stmt('private', function($parse, $token) {
+      $type= $this->statement($parse);
+      $type->modifiers[]= 'private';
+      return $type;
+    });
+
     $this->stmt('#[', function($parse, $token) {
       $annotations= $this->annotations($parse, 'annotations');
       return $this->statement($parse)->annotate($annotations);
diff --git a/src/test/php/lang/ast/unittest/parse/TypesTest.class.php b/src/test/php/lang/ast/unittest/parse/TypesTest.class.php
index cafb2cb..7277e73 100755
--- a/src/test/php/lang/ast/unittest/parse/TypesTest.class.php
+++ b/src/test/php/lang/ast/unittest/parse/TypesTest.class.php
@@ -12,7 +12,7 @@ use lang\ast\nodes\{
   Literal
 };
 use lang\ast\types\IsValue;
-use test\{Assert, Expect, Test};
+use test\{Assert, Expect, Test, Values};
 
 class TypesTest extends ParseTest {
 
@@ -64,6 +64,22 @@ class TypesTest extends ParseTest {
     );
   }
 
+  #[Test, Values(['public', 'private', 'protected'])]
+  public function class_with_visibility($modifier) {
+    $this->assertParsed(
+      [new ClassDeclaration([$modifier], new IsValue('\\A'), null, [], [], null, null, self::LINE)],
+      $modifier.' class A { }'
+    );
+  }
+
+  #[Test]
+  public function public_abstract_class() {
+    $this->assertParsed(
+      [new ClassDeclaration(['abstract', 'public'], new IsValue('\\A'), null, [], [], null, null, self::LINE)],
+      'public abstract class A { }'
+    );
+  }
+
   #[Test]
   public function empty_interface() {
     $this->assertParsed(

Implemetation idea: Name mangling

Emit non-public classes with namespace name so they aren't "visible" to the outside:

namespace com\example;

// Emit this as `__com_example_Parser`
private class Parser { }

When encountering type names, check for private and protected names if the type doesn't exist:

namespace com\example;

class Subject {

  public function parse($input) {
    return new Parser($input); // Emitted as __com_example_Parser
  }
}

This would need some further adjustments to the reflection API, though!

Implemetation idea: Compiler checks

When encountering type names, check xp::$meta for class visibility, and yield errors accordingly:

namespace de\thekid;

use com\example\Parser;

class Subject {

  public function parse($input) {
    return new Parser($input);  // Error: Cannot use private class "com.example.Parser" in "de.thekid"
  }
}

Implemetation idea: Unchecked

Only store visibility modifiers inside xp::$meta but do not check it during compilation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions