Skip to content

Instantly share code, notes, and snippets.

@cretz
Created December 20, 2012 05:23
Show Gist options
  • Save cretz/4343134 to your computer and use it in GitHub Desktop.
Save cretz/4343134 to your computer and use it in GitHub Desktop.

Introduction

What is Pratphall?

Pratphall is a new language targetting PHP. Well, actually it is just TypeScript which is a typed superset of JavaScript. Pratphall cross-compiles TypeScript code to PHP code which means the developer can leverage lots of existing code, patterns, and IDE support that is already built into TypeScript.

Goals

What we're trying to do:

  • Typed PHP
  • Readable and extensible
  • IDE editor support
  • Metaprogramming facilities
  • PHP interoperability
  • Ease of development

What we're NOT trying to do:

  • Fix the PHP standard library or API
  • Emit PHP code that would be annoyingly obtuse or bad quality standing on its own
  • Auto-generate Pratphall code from PHP implementations
  • Add any code to TypeScript

What we'd like to do in the future:

  • Assist code sharing with JS and PHP (e.g. frontend and backend code sharing)
  • Include extremely accurately PHPDoc in emitted output
  • Leverage composer to have built in package manager
  • IDE debugging support

Features

Here are the features in PHP that are supported in Pratphall. This is in a similar order as the PHP manual for easier comprehension.

PHP tags

PHP long opening tags are automatically inserted at the top of every source file. Short tags are never used and there is never a closing tag. Files that use PHP inline for HTML or standard output are not supported. Only pure Pratphall files are supported.

Comments

Comments are supported in the same way they are supported in JS. This means that comments starting with a # are not supported. Due to contraints placed on the parser, comments are only supported at the beginning or end of statements. They are not supported inline. For example:

/**
 * Top level comment
 */
function echoValue(value: string /* some inline doc */) {
    echo('Value: ' + value); // print the value
}

Becomes:

/**
 * Top level comment
 */
function echoValue($value) {
    echo('Value: ' . $value); // print the value
}

Notice the lack of the inline comment.

Types

Booleans

Booleans work the same way they do in TypeScript. Conversions to boolean work the same way they do in PHP.

Integers/Floats

In JS there is no difference between integers and floats for the most part, the same is true in Pratphall. Conversion and testing can still occur at runtime with intval, floatval, and other methods (see Casting below). At compile time however, they are simply numbers. There is no normal way to contrain a variable on one or the other. Hexadecimal literals are translated to normal decimal integers. TODO: more info about negative zero, hex, octal, etc.

Strings

Strings are very similar to the way they are handled in JS. There are no multiline strings. There is no string interpolation (attempts will be escaped). Usually, the preference of single quote or double quote in the Pratphall script is translated verbatim to PHP. However, if there is an escape character in a single-quoted string, it will become double quoted. To request that all strings without escape characters to become single quoted in PHP, use the alwaysPreferSingleQuotes compiler option. See the section on compiler options for more information.

In Pratphall, strings are concatenated with a + sign. In PHP the dot operator is used. Type inference is used to determine when this translation should occur. In Pratphall, if either side of the plus operator is a string, concatenation is assumed. If either side of a plus operator is an unknown/any type a normal arithmetic plus will be used and a compiler warning will be issued.

Any use of the JS String.charAt function is automatically translated at compile time to use string indices in PHP.

Arrays

Unlike PHP, Pratphall differentiates between associative arrays and normal indexed arrays. The former is of type Pct.PhpAssocArray whereas the latter is a normal JS array. This can be confusing at first, but in practice it helps the developer make more accurate assumptions about the typing of variables. You can convert between these types at compile time using Pct.toPhpAssocArray or Pct.toArray. Note when using the latter, until TypeScript had generics the result is any[] so it is recommended to cast it to the exact form of array expected.

Pct.PhpAssocArray's can be created simply: var a = new Pct.PhpAssocArray({ b: 5, c: 10 }); translates to $a = ['b' => 5, 'c' => 10];. Only object literals can be passed to the PhpAssocArray's constructor. The values can be accessed multiple ways. echo(a['a']); and echo(a.b); will both translate to echo($a['b']); in PHP. Using the second way requires type inference, so make sure the a variable is typed as an associative array or Pratphall will thinl it's accessing a property of an object and use the PHP arrow operator.

Indexed arrays are handled almost exactly like they are in JS. They are created with the normal bracketed syntax. When Array.push is used from Pratphall, it's translated to the PHP [] = form.

Many of the JS array features are translated as would be expected to PHP. echo([1, 2, 3].length); is translated to echo(count([1, 2, 3]));. The forEach statement in JS accepts a callback, but when the callback is inline, it is translated properly to PHP. For example [1, 2, 3].forEach((value: number, index: number) => { echo(index + ' => ' value); }); translates to foreach ([1, 2, 3] as $index => $value) { echo($index . ' => ' . $value); } in PHP.

Objects

Objects work very similar to the way they do in PHP and JS. Anonymous object literals become stdClasses via a cast. Example:

var obj = {
    num: 12,
    arr: ['a', 'b', 'c'],
    obj: { innerNum: 12 },
    func: (value: string) { echo('Value: ' + value); }
};

Becomes:

$obj = (object)[
    'num' => 12,
    'arr' => ['a', 'b', 'c'],
    'obj' => (object)[ 'innerNum' => 12 ],
    'func' => function ($value) { echo('Value: ' . $value); }
];

Classes, property accessing, method invocation, etc are all as would be expected. At the time of this writing, TypeScript property accessors are NOT currently supported. Also, classes and interfaces are NOT open-ended like they are in TypeScript. See the limitations section below for more information.

Resources

A PHP resource is represented by the Pct.PhpResource type in Pratphall.

NULL

Null is handled the same way it is in JS and PHP.

Undefined is quite different. The delete JS statement translates directly to PHP, so delete a.b; translates to unset($a->b);. Conditions that check undefined are attempted to translate to safe PHP code that avoids false positives w/ NULL values. So var a = b.c.d === undefined; and var a = typeof b.c.d === 'undefined' both translate to $a = !array_key_exists('d', $b->c);. Note this doesn't use isset, empty, or another construct that accepts non-existent values. So if c is not defined in b, an error will occur in PHP. If you use the unsafeUndefinedTranslate compiler option, an @ will be placed in front of array_key_exists to swallow such errors.

As for isset and empty, they may be used like any other PHP function in Pratphall. Be aware though that isset and empty handle non-existent variables/properties quite well but JS does not. So if you are writing code to share in a JS environment and a PHP environment, avoid these. Of course, most PHP functions should be avoided in shared-with-JS code (unless using something like php.js or something).

Callbacks

Instead of a callable type hint in PHP, callbacks are represented as JS functions. They can be passed around as normal. For example:

class Foo {
    bar(value: string) { echo('Value: ' + value); }
}

var foo = new Foo();
var barFunc = foo.bar;
//call
barFunc('test');

Becomes:

class Foo {
    public function bar($value) { echo('Value: ' . $value); }
}

$foo = new Foo();
$barFunc = (new ReflectionMethod('Foo', 'bar'))->getClosure($foo);
//call
$barFunc('test');

This is safe since unlike PHP, Pratphall does NOT support naming properties/variables and methods/functions the same name. When interoperating with code that does do this, properties are prefixed with 'prop_' to avoid ambiguity. Callable type-hints are still used for parameters expecting functions. Closures can be assigned in PHP exactly like in JS. To avoid interoperability and typing issues, please avoid create_function().

Pseudo-types

Void can be used for return types of functions. There is no such thing as mixed, but there is any which loosely translates. The use of the any type is discouraged as much as possible. When using any you are encouraged to cast to a known type as soon as possible. TypeScript "RestParameters" are supported at the end of function signatures to signify a variable number of arguments especially when defining signatures for methods that already exist. Creation of methods in Pratphall with variable arguments is strongly discouraged since the translation to PHP requires func_get_arg/func_get_args which is not always obvious.

Casting

To cast to a value at compile time, use the TypeScript method. For example: <bool><any>stringValue casts a string to a bool but will not be casted in the emitted PHP code. This is helpful for functions that return a value or FALSE. The test if (<bool><any>strpos('my string', 'q') === false) is a healthy way to determine that a character isn't in a string and will translate to if (strpos('my string', 'q') === false) in PHP. In fact, this is such a common pattern that there is a compile-time helper for it: Pct.isFalse which when used like if (Pct.isFalse(strpos('my string', 'q'))) will translate to the same PHP.

To cast a value at runtime, it is always preferred to use the built-in PHP methods such as strval, intval, etc. If you must have a literal cast, you can use the Pct.to* functions. For example, var a = Pct.toString(false); emits the PHP $a = (string) false;.

Variables

Basics

Variables names are case sensitive and are NOT prefixed with dollar signs (except for references, see later).

Variable scope

All variables in Pratphall are defined somewhere. If they are declared globally and are used in a function, the global keyword is applied in the translated PHP. There is no Pratphall equivalent to the static variable in PHP (not to be confused with the static property/function of a class).

Variable variables

These are not supported in Pratphall in any way. The best you can do is access an object's property by its string-based name using the bracket syntax. So:

var obj = { child: { grandchild: 5 }};
echo('Grandchild val: ' + obj.child['grand' + 'child']);

Becomes

$obj = (object)[ 'child' => (object)[ 'grandchild' => 5 ]];
echo('Grandchild val: ' . $obj->child->{'grand' . 'child'});

Constants

Constants using define in PHP are not supported in Pratphall. The best you can do is a top-level var. TODO: information about interoperability with existing constants.

Magic constants such as FILE are available in the compile time module. They can be accessed like Pct.__FILE__ and are typed as strings.

Expressions

These work as you'd expect in PHP.

!!!!! TODO: take the lists below and make more comprehensive docs !!!!!

Operators

Operators work as expected in PHP except for the following exceptions:

  • clone - Only invoked via Pct.clone and returns "any" type (until TypeScript gets generics) which you should immediately cast back to the type you passed in.
  • . - String concatenation is done via '+' and translated based on type inference. Same goes for .= in PHP which is += in Pratphall.
  • and, xor, or - Not supported in Pratphall
  • Zero-fill right shift - Not supported in PHP or Pratphall, but appears in JS as >>>. This will cause a compiler error.
  • ?: - The normal ternary is supported, but there is no Pratphall equivalent for $a = $possiblyFalse ?: $other;.
  • & - References types and reference assignment are handled differently in Pratphall. See the References section.
  • <> - Not equal in PHP, but unsupported in Pratphall.
  • @ - The @ symbol may not be used to swallow errors in Pratphall. If you must swallow errors, use Pct.swallowErrors (unwieldy name on purpose) which will use the @ sign.
  • Backticks - Unsupported in Pratphall in any way. Use shell_exec if you must.
  • + for arrays - Unioning arrays, if you must, is done with Pct.unionArray. Note, this comes back with any[] if you are using an indexed array, so please cast immediately back to what you expect it to be.
  • ==, !=, === for arrays - These work differently in JS and although it will translate to PHP as written it won't be code you can share with JS. In Pratphall it is recommended to use Pct.array* functions for comparison.
  • instanceof - Only works with class names on the right-hand side, not strings or other instances. If you must do this, use Pct.instanceOf.
  • typeof - This has no operator/construct equivalent in PHP, but it is automatically translated to gettype. Note, the results can differ between JS and PHP, so make sure you code to the PHP environment.
  • ==, != for objects - These work differently in JS and although it will translate to PHP as written it won't be code you can share with JS. Pct.objectEqual is recommended.

Control Structures

These work as you'd expect in PHP except for the following exceptions:

  • elseif - Unsupported in Pratphall, use else if
  • if():, elseif:, else:, endif;, while:, endwhile;, for:, endfor;, foreach:, endforeach;, switch:, endswitch; - Unsupported
  • for...in - This concept isn't present in any way in PHP and isn't worth mounds of runtime code to support. If this is seen, a compiler warning is issued and it's translated to a foreach of array_keys of an array cast. For example, for (a in b) { } translates to foreach (array_keys((array) $b) as $a) { } which is NOT the same thing. Please do not use for...in loops.
  • foreach - This is represented in Pratphall by the forEach function that can be called on an iterable object or an array. The function must be inline, not a reference to another function. So [1, 2, 3].forEach((value: number, index: number) => { }) translates to foreach ([1, 2, 3] as $index => $value) { }. References can still be used, see the References section.
  • break #, continue # - In Pratphall, break and continue statements are optionally associated with labels instead of a number. Normal breaks and continues in Pratphall work as normal. When a label is defined though, the construct becomes a goto. For example, outside: while (a) { while (b) { break outside; } } translates to outside: while ($a) { while ($b) { goto outside; } }.
  • declare - Not a construct in Pratphall, but can be called using Pct.declare. If a function is the last parameter of the call, it considers code within to be in a declare block.
  • global return - Unsupported in Pratphall.
  • include, include_once, require, require_once - No equivalent in Pratphall when used inline. If a /// exists, it will become a require_once if requireReferences compiler option is set. Modules/namespaces are the recommended method of handling dependencies.
  • goto - No equivalent in Pratphall.

Functions

Functions work as you would expect in PHP except for the following exceptions:

  • Inside a function, use of arguments in Pratphall uses func_get_args. For example, function a() { echo('Arg: ' + arguments[0]); } translates to function a() { $arguments = func_get_args(); echo('Arg: ' . $arguments[0]); }.
  • When a "RestParameter" is used in a function, it is actually pulled out of func_get_args. So function a(b: string, c: string, ...d: string[]) { var_dump(d); } translates to function a($b, $c) { $d = array_slice(func_get_args(), 2); var_dump($d); }. Note, trying to use the "RestParameter" as a reference will result in a Pratphall compiler error.
  • When a function is referenced, it is passed around as a closure or instance of ReflectionFunctionAbstract
  • Optional parameters without defaults are assumed to be null.
  • Optional parameters with defaults that are not constant expressions will be set at the beginning of the function in PHP. For example function a(b = doSomething()) { } becomes function a($b = null) { $b = doSomething(); }.
  • Functions that return a reference are prefixed with a dollar sign. References are discouraged. Note, in order for the result to become a reference, Pct.byRef must be used instead of =&. See References section below.
  • Functions that need a parameter as a reference have the parameter name prefixed with a dollar sign. References are discouraged. See References section below.
  • Any variables used in a closure that are not defined with var and reference outside variables will be automatically placed in the uses clause when translating to PHP. References are automatically determined by whether the variable is mutated.
  • Overload signatures are ignored.
  • Type hinting is always used where possible unless typeHintingDisabled is a compiler option. Classes/interfaces that are declared aren't in the typehint (and aren't emitted either). This is an important feature. This means that you could have a LooksLikeMe declared interface and the parameter can be an stdClass and the compiler will guarantee at compile time only that it has all methods, properties, etc that the interface requires. Yet runtime is still dynamic.

Classes and Objects

Classes and interfaces work as you would expect in PHP except for the following exceptions:

  • constructor is the name of __construct. The presence of __construct is a compiler error.
  • Destructors are not explicitly supported. If they are really needed, a simple function called __destruct will work.
  • protected members are not supported. When interoperating w/ external PHP libraries, you will need to make the declared properties/methods private or public based on whether you are actually inheriting or not.
  • All members are public by default.
  • There is no Pratphall equivalent to class constants. Use public static vars.
  • Interfaces can define properties, but cannot initialize them like interface constants.
  • An autoloader and bootstrap is included in Pratphall by default, but of course more can be added.
  • Declared classes and interfaces are not emitted and are healthy for compile-time-only static typing or for representing existing PHP libraries.
  • Static calls change the . to ::.
  • There is no self or static, you must use the class's name. This also means there is no late static binding.
  • The Pratphall equivalent to parent is super.
  • Pratphall doesn't yet support abstract classes or methods (ref TS issue 395).
  • Pratphall doesn't yet support mixins/traits
  • There is an Iterator interface with built in forEach support.
  • TypeScript property accessors are not yet supported (but will be soon and WON'T use __get and __set if I can help it).
  • toString becomes __toString. Presence of __toString is a compiler error.
  • A call signature on the class itself will become __invoke. Presence of __invoke is a compiler error.
  • Besides __construct, __toString, and __invoke, none of the magic methods are explicitly handled by Pratphall and will be translated like any other method.
  • There is no Pratphall equivalent of final.
  • Calls to JSON.parse and JSON.stringify translate to json_decode and json_encode respectively (does it belong here?)

Modules

Known as namespaces in PHP, these work as expected except for the following exceptions:

  • Modules at the top of a file with every non-import, emittable code within makes that a single, semi-coloned namespace at the top of the PHP file.
  • Dots in modules are directly translatable to slashes in PHP.
  • Modules must be at the top-level of the source (no nesting)
  • Modules and classes are automatically placed in PHP files that are easily discoverable during compilation. This is different if the disablePhpStructureReorganization is set as a compilation option
  • Global classes are automatically prefixed with slash when in a namespace scope.
  • Import statements that use module names translate to use statements. However, Pratphall (ref TS issue 119) does not support importing anything other than other modules (cannot import classes or interfaces).
  • There is no Pratphall equivalent of the 'namespace' keyword as an operator.

Exceptions

Called errors in JS, these work as expected except for the following exceptions:

  • Use of the base Error or any of the JS defined errors is translated to the common Exception
  • There is no equivalent of error code in Pratphall
  • Exception is an alias for Error and can be extended easily as needed

Generators

Not yet implemented in Pratphall (ref TS issue 38)

References

Pratphall recommends references be avoided, but if they can't, here's how they are handled:

  • To pass by reference, the parameter name in the function must start with a dollar sign. No checking is done on whether what's sent is valid pass-by-reference material.

  • To return by reference, the function name must start with a dollar sign. Also, to receive the returned result as a reference, you must call Pct.byRef. For example:

    class foo { value = 42; $getValue() { return this.value; } }

    var obj = new foo(); var myValue = Pct.byRef(obj.getValue()); obj.value = 2; echo(myValue);

Becomes:

class foo {
    public $value = 42;
    public function &getValue() { return $this->value; }
}

$obj = new foo();
$myValue = &$obj->getValue();
$obj->value = 2;
echo($myValue);
  • A simple delete becomes an unset, so that's how to unset a reference.

Predefined Variables

All predefined variables are as they appear in PHP, with the following exceptions:

  • Meh...

Predefined Interfaces

All predefined interfaces are as they appear in PHP, with the following exceptions:

  • Any index definition will automatically make the class implement ArrayAccess and put empty offsetExists and offsetUnset functions until provided
  • Traversable has forEach built in
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment