Skip to content

Instantly share code, notes, and snippets.

@thekid
Last active August 29, 2015 13:57
Show Gist options
  • Save thekid/9682745 to your computer and use it in GitHub Desktop.
Save thekid/9682745 to your computer and use it in GitHub Desktop.
PHP: Steps to excellence

A while ago, maybe two years or so at the time of writing, I began thinking PHP was dead. There were releases, and bugfixes, of course, but no noticeable innovation strategy. It's over the zenith, I believed, and that we'd see a slow but steady decline in projects. Time for rethinking technology at home and at work?

Today, it seems my thoughts were too early: The PHP group has since released PHP 5.4, 5.5 and the first alpha of 5.6. Each of them has brought a list of improvements not only on the detail level, but actually meaningful to adapting to nowaday's languages: Short array syntax, traits, method and array call chaining, full closure support, generators (yield), to name just a few. Also, they finally managed to rid themselves of the magic quotes crap. At the same time, each release has brought performance and memory usage improvements. And all that without compromising on stability: The number of critical bugs is perceivedly on an all-time low.

Also, the rise of Composer and its becoming a pseudo-standard has helped a lot on the interoperability of frameworks and components. Much more than centralized PEAR could ever achieve, Composer has fueled reusability in the PHP world. Of course, GitHub has contributed their fair part: Code is no longer sitting hidden in ugly 1999's CVS repositories packed away in .tar.gz files, but shines with a bright light and one-step-install instructions beautifully embedded in Markdown READMEs.

Looking at this makes PHP's future appear in a much brighter light. What next? Here are some thoughts:

Add annotations

Every PHP Framework, in the meantime, has some kind of type, method and member decoration method, usually within the target's api documentation comment. If PHP just supported adding unchecked key-values natively, this would be a great improvement and would make stuff work interchangeably.

class FixtureTests {
  <<Test, Values([1, 2, 3])>>
  function can_create($value) { ... }
}

$m= new ReflectionMethod('FixtureTests', 'can_create');
$m->hasAnnotation('Test');   // true
$m->getAnnotation('Test');   // null
$m->getAnnotation('Values'); // [1, 2, 3]
$m->hasAnnotations();        // true
$m->getAnnotations();        // ['Test' => null, 'Values' => [1, 2, 3]]

Get rid of the stupid "Fatal error: Call to a member-function of a non-object"

This is the primary cause of the "white page" syndrome and the one single PHP un-feature straight from hell. Just throw an exception, really.

$result= $action->execute();
$result->toString();  // Fatals if execute returns NULL

Objectify and de-uglify standard library

PHP's standard library is huge. And as inconsistent as it can get: strrev() vs. str_word_count(), and what's the argument order in in_array() again? Come on. A simple way out of this mess which would not break backwards compatibility beyond repair, clean up the library and at the same time make PHP code more attractive, would be to allow methods on strings, numbers and arrays. The way of having a "procedural" and an object-oriented API with is not new to PHP, the date and XML extensions both offer this dual approach, for example.

// Standardize verbs
$string->length();             // == strlen($string)
$array->length();              // == sizeof($array)

// Fold together alternative implementations
$string->replace('a', 'b')     // == str_replace('a', 'b', $string)
$string->replace(['a' => 'b']) // == strtr($string, ['a' => 'b'])

// Use camel-case
$string->toLower();
$string->trimRight();
@thekid
Copy link
Author

thekid commented Mar 21, 2014

@mikey179
Copy link

The idea of having methods on primitive types is discussed thoroughly in http://nikic.github.io/2014/03/14/Methods-on-primitive-types-in-PHP.html

From my point of view, this also requires scalar type hints. As mentioned in the article it doesn't make much sense to objectify numbers, so we could go with a much looser approach for scalar type hints, so maybe just allowing string as a type hint would be sufficient.

@thekid
Copy link
Author

thekid commented Apr 20, 2014

On Fatal error: Call to a member-function... I've created pull request #647. It passes all tests contrary to the arguments of earlier times that this leaves the engine in an indeterminate state.

@thekid
Copy link
Author

thekid commented Apr 20, 2014

Other runtime fatal errors:

String offsets

  • Cannot use string offset as an object ($a= "Hello"; $a[0]->prop+= 1;)
  • Cannot use string offset as an array ($a= "Hello"; $a[0][]= 1;)
  • Cannot increment/decrement overloaded objects nor string offsets ($a= "Hello"; $a[0]++;)
  • Cannot unset string offsets ($a= "Hello"; unset($a[0]);)
  • Cannot create references to/from string offsets nor overloaded objects ($a= "Hello"; $r= &$a[0];)
  • Cannot use assign-op operators with overloaded objects nor string offsets ($a= "Hello"; $a[0]+= 1;')
  • Cannot yield string offsets by reference (function &a() { $a= "Hello"; yield $a[0]; } foreach (a() as $a) { })
  • Cannot return string offsets by reference (function &a() { $a= "Hello"; return $a[0]; } a();')

Arrays

  • Cannot use [] for reading ($a= []; $b= $a[];)
  • Cannot use [] for unsetting (unset($a[]);)
  • ✅ (via ArrayAccess) Cannot use object of type a as array - (class a { } $a= new a(); unset($a[0]);)
  • Undefined offset for object of type %s used as array (with ArrayAccess after offsetGet() invocation not returning)
  • Illegal offset type - ($a= []; $a[[]];)

Invocations

  • Cannot call abstract method %s::%s()
abstract class a { abstract function method(); }
class b extends a { function method() { return parent::method(); }}
(new b())->method();
  • Non-static method %s::%s() cannot be called statically (only for internal functions, E_WARNING for userland)
  • Cannot call overloaded function for non-object (?)
  • Object does not support method calls (?)
  • ✅ (via __call) - Call to undefined method %s::%s() (class a { } (new a())->method();)
  • ✅ (via __call) - Call to %s method %s::%s() from context '%s' (with private&protected)
  • Call to a member function %s() on a non-object ($a= null; $a->method();)
  • ✅ (via __autoload) Class '%s' not found (a::method();)
  • Cannot call constructor `
class a { }
class b extends a { function __construct() { parent::__construct(); }}
new b();
  • Cannot call private %s::__construct()
class a { private function __construct() { } }
class b extends a { function __construct() { parent::__construct(); }}
new b();
  • Call to undefined function %s() - func();
  • Cannot get arguments for __call - ?

Dynamic naming

  • Class name must be a valid object or a string ($a= []; new $a();)
  • Method name must be a string (class a { } $a= new a(); $a->{null}();)
  • Cannot access empty property (class a { } $a= new a(); $a->{null};)
  • Cannot access property started with '\0' (class a { } $a= new a(); $name= "\0member"; $a->{$name}; - prevents accessing private and protected)
  • Call to undefined function %s() - $f= 'func'; $f();
  • Function name must be a string - $f= []; $f();
  • First array member is not a valid class name or object - $f= [null, "method"]; $f();
  • Second array member is not a valid method - $f= ["a", null]; $f();

Properties

  • ✅ (via __get/__set) - Cannot access %s property %s::$%s - (class a { private $p; } $a= new a(); $a->p;)
  • Access to undeclared static property: %s::$%s (class a { } a::$prop;)
  • Attempt to unset static property %s::$%s (class a { static $prop; } unset(a::$prop);)

Parameters

  • Cannot pass parameter %d by reference - ?
  • Only variables can be passed by reference - function a(&$r) { } a([]);
  • ✅ (via E_RECOVERABLE_ERROR) - Cannot unpack array with string keys - (function f(... $a) { } f(...["a" => "b"]);)
  • ✅ (via E_RECOVERABLE_ERROR) - Cannot unpack Traversable with string keys - (function f(... $a) { } f(...new ArrayObject(["a" => "b"]));)
  • Unknown typehint - (unreachable code)
  • ✅ (via E_RECOVERABLE_ERROR) - ... be of the type array
  • ✅ (via E_RECOVERABLE_ERROR) - ... be of the type callable
  • ✅ (via E_RECOVERABLE_ERROR) - ... be of the type %s

Instantiation

  • Cannot instantiate interface %s - interface a { } new a();
  • Cannot instantiate trait %s - trait a { } new a();
  • Cannot instantiate abstract class %s - abstract class a { } new a();
  • Call to private %s::%s() from invalid context - class a { private function __construct() { }} new a(); (from main scope)
  • Call to private %s::%s() from context '%s' (same as above, but from other class)
  • Call to protected %s::%s() from invalid context - class a { protected function __construct() { }} new a(); (from main scope)
  • Call to protected %s::%s() from context '%s' (same as above, but from other class)

Exceptions

  • Exceptions must be valid objects derived from the Exception base class - (class a { } throw new a();')
  • Exception thrown without a stack frame - (?)
  • Can only throw objects (throw "Hello";)
  • Attempt to destruct pending exception (?)
  • Cannot set non exception as previous exception (?)
  • Wrong parameters for Exception - (new Exception([]);)
  • Wrong parameters for ErrorException - (new ErrorException([]);)
  • Bad class name in the catch statement - (?)
  • Cannot use try without catch or finally - (try { })

Cloning

  • __clone method called on non-object - clone 1;
  • Trying to clone an uncloneable object of class %s - ?
  • Trying to clone an uncloneable object - ?
  • Call to private %s::__clone() from context '%s' - class a { private function __clone() { } } clone new a();
  • Call to protected %s::__clone() from context '%s' - class a { protected function __clone() { } } clone new a();

Constants

  • Undefined class constant '%s' - class a { } a::CONSTANT;
  • Undefined constant '%s' - \CONSTANT;
  • Arrays are not allowed in constants at run-time - ?
  • Cannot declare self-referencing constant '%s' - (class a { const C = self::C; } a::C;)
  • Arrays are not allowed in class constants (class a { const L = []; })
  • Unsupported constant expression - (?)

Declaration

  • %s cannot implement %s - it is not an interface - (class b { } class a implements b { })
  • %s cannot use %s - it is not a trait - (class b { } class a { use b; })
  • Interface %s cannot implement itself - (interface a extends a { })
  • Interface %s may not inherit from class (%s)
  • Class %s cannot extend from interface %s
  • Class %s cannot extend from trait %s
  • Class %s may not inherit from final class (%s)
  • Class %s cannot implement previously implemented interface %s
  • %s has colliding constructor definitions coming from traits
  • Declaration of %s must be compatible with %s
  • Cannot inherit previously-inherited or override constant %s from interface %s
  • Cannot access self:: when no class scope is active
  • Cannot access self::class when no class scope is active
  • Cannot access static:: when no class scope is active
  • Cannot access parent:: when current class scope has no parent
  • Cannot access parent:: when no class scope is active
  • ✅ (via autoloading) Trait '%s' not found - (class a { use b; })
  • ✅ (via autoloading) Interface '%s' not found - (class a implements b { })
  • ✅ (via autoloading) Class '%s' not found - (class a extends b { })
  • Class %s contains %d abstract method%s and must therefore be declared abstract or implement the remaining methods (...) - (class a { abstract function method(); })
  • "%s function %s::%s() cannot be declared private (abstract class a { abstract private function method(); })
  • %s function %s::%s() cannot contain body - (abstract class a { abstract function method() { } })
  • Non-abstract method %s::%s() must contain body - (class a { function method(); })
  • Multiple access type modifiers are not allowed
  • Multiple abstract modifiers are not allowed
  • Multiple static modifiers are not allowed
  • Multiple final modifiers are not allowed
  • Cannot use the final modifier on an abstract class member
  • Access type for interface method %s::%s() must be omitted - (interface a { private function method(); })
  • Cannot redeclare %s::%s() - (class a { function method() { } function method() { } })
  • Cannot declare function %s because the name is already in use - (?)
  • Cannot redeclare method() (previously declared in ...) - (function method() { } function method() { })
  • ZEND_AUTOLOAD_FUNC_NAME () must take exactly 1 argument - (function __autoload($name, $extra) { })
  • '%s' is an invalid class name" - (?)
  • Call-time pass-by-reference has been removed - (a(&$r);)
  • Cannot override final %s::%s() with %s::%s()
  • Cannot override final method %s::%s()
  • Can't inherit abstract function %s::%s() (previously declared abstract in %s)"
  • Cannot make non static method %s::%s() static in class %s
  • Cannot make static method %s::%s() non static in class %s
  • Cannot make non abstract method %s::%s() abstract in class %s
  • Access level to %s::%s() must be %s (as in class %s)%s
  • Declaration of %s::%s() must be compatible with %s
  • Cannot redeclare %s%s::$%s as %s%s::$%s
  • Access level to %s::$%s must be %s (as in class %s)%s
  • Trait method %s has not been applied, because there are collisions with other trait methods on %s
  • Trait method %s::%s has not been applied as %s::%s, because of collision with %s::%s
  • Class %s is not a trait, Only traits may be used in 'as' and 'insteadof' statements
  • Required Trait %s wasn't added to %s
  • An alias was defined for %s::%s but this method does not exist
  • Failed to evaluate a trait precedence (%s). Method of trait %s was defined to be excluded multiple times
  • Class declarations may not be nested
  • Cannot use '%s' as class name as it is reserved
  • Cannot use 'self' as class name as it is reserved
  • Cannot use 'parent' as class name as it is reserved
  • Cannot use 'static' as class name as it is reserved
  • Cannot redeclare class %s
  • A trait (%s) cannot extend a class. Traits can only be composed from other traits with the 'use' keyword. Error
  • Constructor %s::%s() cannot be static
  • Destructor %s::%s() cannot be static
  • Clone method %s::%s() cannot be static
  • Cannot use '%s' as interface on '%s' since it is a Trait
  • Cannot use '%s' as interface name as it is reserved
  • Cannot use '%s' as trait name as it is reserved
  • Interfaces may not include member variables
  • Properties cannot be declared abstract
  • Cannot declare property %s::$%s final, the final modifier is allowed only for methods and classes
  • Cannot redeclare %s::$%s
  • Traits cannot have constants
  • Cannot redefine class constant %s::%s
  • __HALT_COMPILER() can only be used from the outermost scope
  • "static::" is not allowed in compile-time constants - (class a { const L = static::A; })
  • Cannot mix bracketed namespace declarations with unbracketed namespace declarations
  • Namespace declarations cannot be nested
  • Namespace declaration statement has to be the very first statement in the script
  • Cannot use '%s' as namespace name
  • Cannot use %s as %s because '%s' is a special class name
  • Cannot use %s as %s because the name is already in use
  • You seem to be trying to use a different language...
  • Cannot redeclare constant '%s'
  • Cannot declare const %s because the name is already in use
  • No code may exist outside of namespace {}
  • Key element cannot be a reference (foreach ([1] as &$k => $v) { })
  • Cannot use list as key element (foreach ([1] as list($k) => $v) { })
  • Cannot use empty list (foreach ([1] as list()) { })

Encoding

  • Could not convert the script from the detected
  • Cannot use constants as encoding
  • Encoding declaration pragma must be the very first statement in the script"

Lambdas

  • Base lambda function for closure not found - ?
  • Cannot yield from finally in a force-closed generator - ?
  • Cannot resume an already running generator - ?
  • Cannot destroy active lambda function - ?
  • ✅ (via E_RECOVERABLE_ERROR) Cannot get arguments for calling closure - ?
  • ✅ (via E_RECOVERABLE_ERROR) Closure object cannot have properties - ($f= function() { }; $f->prop;)
  • ✅ (via E_RECOVERABLE_ERROR) Instantiation of 'Closure' is not allowed - (new Closure())
  • The "yield" expression can only be used inside a function - (yield 1;)
  • Cannot use $this as lexical variable

Operations

  • Object of class a could not be converted to string - (class a { } $a= new a(); var_dump((string)$a);)
  • Unsupported operand types ("Hello" + [];)
  • ZEND_DEBUGINFO_FUNC_NAME "() must return an array" - (class a { function __debugInfo() { return null; }} var_dump(new a());)
  • Method %s::__toString() must not throw an exception - (class a { function __toString() { throw new Exception(""); }} (string)new a();)
  • ✅ (via E_RECOVERABLE_ERROR) Method %s::__toString() must return a string value - (class a { function __toString() { return null; }} (string)new a();')
  • Cannot re-assign $this - (class a { function method() { $this= null; } (new a())->method();)
  • Can't use function return value in write context - (class a { static function method() { }} a::method() = 1;)
  • Can't use method return value in write context - (class a { function method() { }} $a= new a(); $a->method() = 1;)
  • instanceof expects an object instance, constant given
  • Cannot use isset() on the result of a function call (you can use "null !== func()" instead) - isset(a());
  • Cannot use isset() on the result of an expression (you can use "null !== func()" instead) - isset(a= 1);
  • Using $this when not in object context - $this->x;

Memory

  • String size overflow (concatenating two strings results in string > MAX_INT)
  • Nesting level too deep - recursive dependency? (?)
  • Balloc() allocation exceeds list boundary (?)
  • Balloc() failed to allocate memory (?)
  • Class entry requested for an object without PHP class (?)
  • Corrupted fcall_info provided to zend_call_function() (?)
  • Error installing signal handler for %d (?)
  • Unexpected inconsistency in create_function() (?)
  • Maximum execution time of %d second%s exceeded (with EG(timeout))
  • Invalid opcode %d/%d/%d. (when hacking on internals)
  • Arrived at end of main loop which shouldn't happen (Unreachable code)
  • Possible integer overflow in memory allocation (%zu * %zu + %zu) (?)
  • Internal Zend error - Missing class information for %s (?)

Control flow

  • jump into a finally block is disallowed - (goto a; try { } finally { a: })
  • jump out of a finally block is disallowed - (try { } finally { goto a; } a:)
  • Cannot break/continue %d level%s - (foreach ([1] as $a) { break 2; })
  • Label '%s' already defined - (a: a:)
  • 'goto' to undefined label '%s'" - (goto a;)
  • 'goto' into loop or switch statement is disallowed - (goto a; while (0) { a: }')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment