and why you should
PHP is fun for small prototypes and hacks.
But building large software systems is a problem
reliability, security, maintainability... fun
This is not a problem only for PHP software
It is a problem of failing software design
So why does it fail?
"Whatever we [in computing] do is more like what the Egyptians did. Building pyramids, piling things on top of each other."
Alan Kay - Programming and Scaling
http://www.tele-task.de/archive/video/flash/14029/
"Complexity is the single major difficulty in the successfull development of large-scale software system"
- 'Out of the tar pit' - B. Moseley, P. Marks
intertwining multiple concerns
Complect:
To join by weaving or twining together; interweave.
Simple made easy - Rich Hickey
http://www.infoq.com/presentations/Simple-Made-Easy
Software complexity:
Ben Moseley / Peter Marks - 2006
http://shaffner.us/cs/papers/tarpit.pdf
global variables, closures,
objects, iterators
no shared state
there are no variables
every function call will return exactly the same result given the same arguments
there are no side effects
... in a pure functional programming language anyway
Object Oriented Programming
anything goes, but:
if/then/else, while/for, try/catch
recipe style: first do this, then do that
declarative style: specify what, not how
recursion as the only loop mechanism
loops explicitly depend on characteristics of the data
complex vs. simple
class Application_Form_Guestbook extends Zend_Form { public function init() { // Set the method for the display form to POST $this->setMethod('post'); // Add an email element $this->addElement('text', 'email', array( 'label' => 'Your email address:', 'required' => true, 'filters' => array('StringTrim'), 'validators' => array( 'EmailAddress', ) )); ...
$form = $this->createFormBuilder($task) ->add('task', 'text') ->add('dueDate', 'date', array(
'required' => true
) ) ->getForm();
$form = [ 'fields' => [ 'name' => [ 'type' => 'text', 'required' => true ] ] ];
now it is trivial to add support for any form code
- zend or symfony or ...
$form = new MyForm([ 'fields' => [ 'name' => [ 'type' => 'text', 'required' => true ] ] ]); if ( $form->isSubmitted() && $form->isValid() ) { echo 'Thank you'; } else { echo $form; }
tr -cs A-Za-z '\n' |
tr A-Z a-z |
sort |
uniq -c |
sort -rn |
sed ${1}q
"the defect density of code, bugs per hundred lines, tends to be a constant independent of implementation language"
- Eric S. Raymond
"Program testing can convincingly show the presence of bugs but it is hopelessly inadequate to show their absence."
- E. Dijkstra
so writing less code is more important
than writing more unit tests
domain specific languages can drastically reduce code size and complexity
cairo: graphics library written in c
code size in 100.000's lines of code
jitblt/gezira: hundreds lines of code in specific dsl, performance similar to cairo.
has the potential to write low volume code
- the no-framework MVC framework, Rasmus Lehrdorf -
it is already a DSL for websites
but...
or library hell happened
example: ezPublish 5
- developer created optimization to speed up PHP's autoloading.
why?
- ezPublish 5 contains > 2600 classes
partially based on Symfony 2
php is a template language
but: Smarty, Twig, Moustache, ... or:
function parseTemplate( $template, $arguments ) { $regex = '\{\{(.*)\}\}'; return preg_replace_callback( '/'.$regex.'/', function( $matches ) use ( $arguments ) { return isset( $arguments[ $matches[1] ] ) ? $arguments[ $matches[1] ] : ''; }, $template ); }
Guzzle, Buzz, Zend, Symfony, or:
function httpRequest( $url, $options ) { $options += [ 'method' => 'GET' ]; $context = stream_context_create( array( 'http' => $options ) ); $response = @file_get_contents( $url, false, $context ); return [ 'response' => $response, 'headers' => $http_response_header ]; }
yagni - you ain't gonna need it
scale out in abstraction rather than verbosity.
"Composing simple components is the key to robust software" - Rich Hickey
PHP is embracing components
Composer, Packagist, Laravel, Aura, etc.
see: https://packagist.org/explore/popular
but: quality and complexity vary wildly
Ariadne Components - but also "Architecture"
a set of simple components for the most common web application related tasks
url, http, html, xml, configuration, events, grants
short answer: we needed a way forward for Ariadne
Ariadne stores content in a virtual filesystem
so all components need to understand filesystem-like hierarchies
most currently available components don't
ARC tries to embrace 'simple'
uses a few well-defined abstractions:
path, url, tree
each with functional programming style operations
ARC is small
biggest files are
classes
ARC is not a framework
doesn't try to be
- no routing
- no mvc
- no orm
just the bricks, none of the knitting
html writer
function menu( $list ) { return \arc\html::ul( [ 'class' => 'menu' ], array_map( function( $child ) { return \arc\html::li( \arc\html::a( [ 'href' => $child['url'] ] , $child['name'] ) ) }, $list ) ); }
$view = \arc\lambda::prototype( [
'class' => 'menu', 'menu' => function( $children ) { return \arc\html::ul( ['class' => $this->class], array_map( $this->menuitem, (array) $children ) ); }, 'menuitem' => function( $input ) { return \arc\html::li(
\arc\html::a(
[ 'href' => $input['url'] ],
$input['name']
), ( isset( $input['children'] ) ? $this->menu( $input['children'] ) : null ) ); }, ] ); echo $view->menu( $menulist );
$result = \arc\path::parents( '/a/b/c/' );
// => '/a/', '/a/b/', '/a/b/c/' $result = \arc\path::reduce( '/a/b/c/', function( $result, $item ) { return $result . '[' . $item . ']'; }, '' ); // => '[a][b][c]' $result = \arc\path::collapse( '/a/../b/' ); // => '/b/'
$tree = [ '/a/' => 'Item A', '/a/b/c/' => 'Item C', '/d/e/' => Item D' ]; $root = \arc\tree::expand( $tree ); $result = \arc\tree::walk( $root, function( $node ) { if ( strpos( 'C', $node->nodeValue ) !== false ) { return $node; } }); // => node( 'Item C' ) $result->nodeValue = 'Item C changed'; $tree = \arc\tree::collapse( $root ); // => [ // '/a/' => 'Item A', // '/a/b/c/' => 'Item C changed', // '/d/e/' => 'Item D' // ]
$listener = \arc\events::cd( '/test/' ) ->listen( 'testEvent', function($event) { $event->data['seen'] = true; } ); $result = \arc\events::cd( '/test/child/' )
->fire( 'testEvent', array( 'seen' => false ) ); if ( $result ) {
// event not cancelled
}
$proxy = \arc\cache::proxy( new ComplexObject(), 3600 ); //first call $result = $proxy->takesTooLong(); //second call is cached $result2 = $proxy->takesTooLong();
Because of ducktyping arc accepts proxies wherever any other object is expected.
$user = 'test'; $userGrants = \arc\grants::user( $user ); $userGrants->cd( '/' )->setUserGrants('read edit add >delete'); ... if ( $userGrants->check('read') ) { // has read grant at the current path. }
still experimental
lots of ideas have been tried, many rejected:
- php-option
- null objects
- duck typing by __implements
ease of use also important
http://ariadne-cms.github.com/arc/
https://packagist.org/packages/arc/arc
(somewhat stable version)
https://github.com/poef/arc
( more experimental version )
https://joind.in/8098
http://www.rvl.io/poefke/simple-php-components/