code.ivysaur.me
php2go
Convert PHP source code to Go by AST walking.
The goal is to produce idiomatic, maintainable Go code as part of a one-off conversion. This is not generally possible for highly dynamic PHP code, that may require manual fixups.
Progress
Phase 1
- [X] Convert some small programs
- [X] Error handling
- [X] All functions return
(type, error)
- [X] Convert
throw
to err return - [X] Non-leaf function calls need to check + bubble errors
- [X] All functions return
- [X] Array handling
- [X] Infer whether to use slice/map for PHP array
- [-] Namespaces
- [X] Basic support for
namespace
-> package name transformation - [ ] Resolve namespace names in calls
- [ ] Resolve
use
statements
- [X] Basic support for
- [X]
if
- [X]
for
/foreach
- [X]
switch
- [ ] Generators
- [ ] Goto
- [X]
null
->nil
- [X] Consts and
define()
- [X] Magic constants (
__LINE__
,__FILE__
etc) - [ ] Predefined constants (
PHP_VERSION
,PHP_EOL
,PHP_OS
,PHP_INT_MAX
etc)
- [X] Magic constants (
- [ ]
isset
/unset
- [ ]
instanceof
- [X] Ternary expressions
- Implemented as inline closure
- [X]
die
- [X]
try
/catch
/finally
- [X] Implement using err checks, not panic/recover
- Any statements within the try block should capture the finally/catch to run within any interior err-check blocks
- [ ]
finally
- Runs before any return statement inside the try block or catch block; also runs after the try block is complete
- Maybe
finally
would be more concisely implemented as defer inside an IIFE, but that is not idiomatic, maintainable Go code
- [X] Implement using err checks, not panic/recover
- [X] Abandon upon sight of highly dynamic constructs
- [X]
eval
- [X]
extract
- [ ] variable-variables
- [ ] Attempts to enable magic_quotes or register_globals
- [X]
- [X] Detect assignment expressions
- Go doesn't support assignment in rvalues, only as a statement
- When walking below stmt level, need to first fully walk and check for any function calls + assignments that may need hoisting (and we can probably only do that correctly if there is no short-circuiting)
- [X] Add subtree walk + catch error for this case
- [X] Closures
- [ ] Handle value/reference captures
- [X] Convert top-level calls to
init()
/main()
- [X] Wrap in function
- [X] Determine init/main based on package name
- [ ] Class/object transformations
- [X] Convert
new X
constructor calls toNewX
- [X] Visibility
- [X] Apply PHP visibility modifiers as Go case change
- [ ] Call mangled function name at call sites
- PHP variable names are case-sensitive, function names are case-insensitive
- [X] Static methods
- [X] Inheritance partial
- [X] Interfaces
- [X] Class constants
- [X] super
- [X] Need to track current conversion state through into function generator, to select the concrete parent
- [X] parent::
- [X] self::
- [ ] static::
- [ ] Traits /
use
- [ ] Abstract methods
- [X] Convert
- [X] Track golang package imports
- [X] Variadic function parameters
- [X] Preserve comments
Productionize
- [ ] Multi-file programs
- [ ] Include/Require
- [ ] Infer whether to declare variable (
var
/:=
) or reuse (=
)- [ ] Track current visibility scope
- [ ] Comprehensive coverage of all AST node types
- [X] Create full-coverage interface to implement
- [ ] Node
- [ ] Stmt
- [ ] Expr
- [ ] Assign
- [X] Binary
- [X] Scalar
- [X] Heredocs/nowdocs
- [ ] Numbered break/continue
- [ ] Type inference
- [ ] Type declarations for literals (string, slice/map with constant initializer, etc)
- [ ] Convert known PHP typenames to Go equivalents
- [ ] Parse extra types from phpdoc blocks
- [X] Hoisting pass for rvalue expressions
- [X] Preincrement statements
- [ ] Behaviour of function calls in rvalue cases where err cannot be checked
- [ ] Assignment expressions
- [ ] Alternately - we can support the form
if foo := bar(); foo {
- [ ] Alternately - we can support the form
- [ ] Simple standard library transformations
- [X] common string/array functions
- [ ] More string/array functions
- [ ] Top 100 from https://www.exakat.io/top-100-php-functions/
- [X] substr -> slice index operator
- This has the same value semantics for ~~string literals, but not variables~~ all cases
- [ ] error_reporting(E_ALL) --> just remove it
- [ ] ini_set('display_errors', 'On') --> remove it when the parameter is known
- [ ] Elide error return for functions that cannot throw
- ?? Could be a standalone Go refactoring tool
Moonshots
- [ ] Extended library/environment transformations
- [ ] Standard library transformations
- [X] Track golang package imports
- [ ] Handle conflicts between golang stdlib packages / local variable names
- [ ] uasort -> sort.Slice
- [ ] preg -> regexp
- [ ] json -> encoding/json
- [ ] Output buffering (
ob_start
/ob_get_clean
)- Can push/pop os.Stdout onto a private stack, then we can keep using
fmt.Print
- Can push/pop os.Stdout onto a private stack, then we can keep using
- [ ] PHP Superglobal transformations
- [ ]
$_SERVER['argv']
to os.Args - [ ]
$_GET['name']
to r.FormValue('name')
- [ ]
- [ ] Common 3rd party package transformations to popular equivalents (optional)
- [ ] Guzzle
- [ ] AWS SDK
- [ ] PHPUnit -> Go Test
- [ ] Replace Composer/PSR-4 autoloading with Go imports
- [ ] Standard library transformations
- [ ] Preserve rough line spacing
- [ ] Somehow detect whether map use should be order-preserving
- [ ] Support scoped namespace (
namespace { ... }
) and multiple namespaces in file - [ ] Validation on imported comment formats
- [ ] Convert wordpress / mediawiki / symfony
- [ ] Option to convert with preset=cli, preset=web, webroot path
- [ ] Option to generate built-in web server
- [ ] Generate source maps
- The parser does have full positional information - exporting at the stmt level would be reasonable
- But the gofmt pass may lose this information
- [ ] Command-line tool option to
go run