HACKER Q&A
📣 anvius

A PHP Superset Language


I'm developing a PHP transpiler and I've created a superset called Simple PHP Superset (SPS). The goal is not to replace PHP but to extend it—making the code easier to read and maintain while preserving full PHP compatibility. Our company’s codebase suffers from numerous issues, and I believe that SPS can help solve these by introducing modern improvements without breaking existing PHP libraries.

Here are the most features of SPS:

- Full Compatibility with PHP 8:

You can mix traditional PHP code (with braces, semicolons, etc.) with SPS syntax. In strict mode, warnings are emitted if you don’t follow the recommended style.

- Minimalist, Clean Syntax:

  + Use `end` instead of braces to close blocks.  
  + Instantiate classes by calling them like functions (no need for `new`).  
  + Use `raise(...)` in place of `throw new`.
- Private by Default:

All members (variables, functions, properties) are private by default. Use the keyword `pub` to expose them publicly.

- Immutable by Default:

  + Variables declared with `var` are immutable once assigned, and only variables declared with `mut` can be modified.  
  + Function parameters intended to be modified must be marked as `mut` (transpiling to PHP by-reference).
- Strict Static Typing & Union Types:

You must declare explicit types (e.g. `int`, `string`) and use union types (e.g. `int|string`) for enhanced compile‑time safety.

- Modern Data Structures & Manipulation:

  + Slicing: Extract subarrays using the notation `[start:end]` (similar to Python).  
  + Maps & JSON-like Objects: Use the `{ key: value }` notation to define maps and object literals, which is cleaner than PHP’s `=>`.  
  + Destructuring: Easily extract values from arrays, tuples (using parentheses), and JSON-like objects (using braces).
- Named Parameters:

Functions can be called with named parameters in any order for improved clarity.

- Concurrency with Coroutines:

Launch concurrent tasks with `php(expr)`, which executes the expression in a Fiber (PHP 8.1–based). Use a scheduler (e.g. `Scheduler.joinAll()`) to wait for all coroutines to complete.

- Enhanced Functional Programming:

An unified standard library (`Standard\`) provides higher‑order functions like `map`, `filter`, and `reduce` in camelCase, with a logical parameter order. Also, most of PHP functions implemented with a logical order.

- System Types for Resources:

It includes classes like `System\File` to wrap system resources (files, sockets, streams) in an object‑oriented manner, providing methods like `read()`, `write()`, and `close()`, with automatic error handling.

- Composition with `compose`:

Emulating Go’s embedding, use `compose` to inject the behavior of a normal class into another. For example, `compose Logger` in a class will automatically create an internal field and delegate its public methods so you can call `this.log(...)` directly without referencing the internal variable.

- Multiple Return Values:

Functions can return multiple values via tuples, making it easier to communicate results without resorting to arrays or ad‑hoc structures.

I've designed SPS to modernize PHP by providing a cleaner (specially to avoid usual mistakes in our codebase), more expressive syntax along with stricter static typing, immutability by default, named parameters, slicing, JSON‑like maps and objects, and enhanced functional programming features—all while being fully compatible with existing PHP code. Wiping code of `I'm actively developing the transpiler and would love to hear feedback from the community. Does this approach make sense? What aspects should I improve or change? Should I abondon it? Any input would be greatly appreciated!

Thank you!


  👤 psychoslave Accepted Answer ✓
Do you have some repository yet?

👤 anvius
Here an example code:

  // NOTE: $this file is provided as a single file as an example only.
  // Classes should be publicly accessible and not private, but in $this example it's not necessary.
  // It is possible add composition of classes go-style with compose.

  namespace Domain

  use \StdPS\String
  use \StdPS\Array
  use \StdPS\Time
  use \StdPS\Scheduler

  class Contact
      var $id: string
      var $name: string
      var $email: string

      construct($id: string, $name: string, $email: string): void
          $this.id = $id
          $this.name = $name
          $this.email = $email
      end

      pub func updateEmail($newEmail: string): void
          $this.validateEmail($newEmail)
          $this.email = $newEmail
      end

      toString(): string
          return String.Join(["Contact(id:", $this.id, ", name:", $this.name, ", email:", $this.email, ")"], " ")
      end

      func validateEmail($newEmail: string): void
          if (!String.Email.Validate($newEmail))
              raise(InvalidArgumentError, "Invalid email address: " + $newEmail)
          end
      end

  end

  interface ContactRepository
      pub func save($contact: Contact): void
      pub func findById($id: string): Contact|void
      pub func findAll(): [Contact]
  end

  class InMemoryContactRepository: ContactRepository
      mut $contacts: [string:Contact]

      construct(): void
          $this.contacts = []
      end

      pub func save($contact: Contact): void
          $this.contacts[$contact.id] = $contact
      end

      pub func findById($id: string): Contact|void
          if (isset($this.contacts[$id]))
              return($this.contacts[$id])
          end
          return(null)
      end

      pub func findAll(): [Contact]
          return($this.contacts)
      end
  end

  pub class ContactService
      var $repository: ContactRepository

      construct($repo: ContactRepository): void
          $this.repository = $repo
      end

      pub func registerContact($id: string, $name: string, $email: string): void
          var $contact = Contact($id, $name, $email)
          $this.repository.save($contact)
      end

      pub func getContactInfo($id: string): string|void
          var $contact = $this.repository.findById($id)
          if ($contact)
              return($contact.toString())
          end
          return(null)
      end

      pub func listAllContacts(): [Contact]
          return($this.repository.findAll())
      end
  end

  mut $repo = InMemoryContactRepository()
  var $service = ContactService($repo)

  $service.registerContact($id="1", $name="Alice", $email="alice@example.com")
  $service.registerContact($id="2", $name="Bob", $email="bob@example.com")

  echo(String.Join(["Contact Info:", $service.getContactInfo("1")], "\n"))

  echo("\nAll Contacts:\n")
  var $all = $service.listAllContacts()
  foreach($all, $contact)
      echo($contact.toString(), "\n")
  end

  var $numbers = [10,20,30,40,50]
  var $subNumbers = $numbers[1:4]  // Expecting [20,30,40]
  echo("Sliced Numbers: " + String\Join($subNumbers, ", "))

  func simulateTask($id: int): void
      Time\Sleep(1)
      echo("Task " + $id + " completed")
  end

  php(simulateTask(1))
  php(simulateTask(2))
  echo("Launched tasks concurrently.")
  Scheduler.JoinAll()