PHP Micro-Framework Cheat Sheet
Total Page:16
File Type:pdf, Size:1020Kb
Cheat Sheet SILEX PHP micro-framework http://andreiabohner.org Install nginx IIS web.config file: $ composer require silex/silex:~1.2 configure your vhost to forward non-existent resources to index.php: <?xml version="1.0"?> <configuration> server { <system.webServer> #site root is redirected to the app boot script <defaultDocument> location = / { <files> Usage try_files @site @site; <clear /> } <add value="index.php" /> // web/index.php </files> require_once __DIR__.'/../vendor/autoload.php'; # all other locations try other files first and go to our </defaultDocument> # front controller if none of them exists <rewrite> $app = new Silex\Application(); location / { <rules> try_files $uri $uri/ @site; <rule name="Silex Front Controller" $app->get('/hello/{name}', function ($name) use ($app) { } stopProcessing="true"> return 'Hello ' . $app->escape($name); <match url="^(.*)$" ignoreCase="false" /> }); #return 404 for all php files as we do have a front controller <conditions logicalGrouping="MatchAll"> location ~ \.php$ { <add input="{REQUEST_FILENAME}" $app->run(); return 404; matchType="IsFile" ignoreCase="false" } negate="true" /> </conditions> location @site { <action type="Rewrite" url="index.php" fastcgi_pass unix:/var/run/php-fpm/www.sock; appendQueryString="true" /> Web Server Configuration include fastcgi_params; </rule> fastcgi_param SCRIPT_FILENAME $document_root/index.php; </rules> Apache #uncomment when running via https </rewrite> #fastcgi_param HTTPS on; </system.webServer> <IfModule mod_rewrite.c> } </configuration> Options -MultiViews } only for RewriteEngine On PHP 5.4 built-in webserver development! #RewriteBase /path/to/app RewriteCond %{REQUEST_FILENAME} !-f Allows run silex without any configuration. However, in RewriteRule ^ index.php [QSA,L] Lighttpd order to serve static files, you'll have to make sure your </IfModule> front controller returns false in that case (web/index.php): simple-vhost: $filename = __DIR__.preg_replace('#(\?.*)$#', '', server.document-root = "/path/to/app" $_SERVER['REQUEST_URI']); If your site is not at the webroot level: if (php_sapi_name() === 'cli-server' && is_file($filename)) { uncomment the RewriteBase statement and adjust the url.rewrite-once = ( return false; path to point to your directory, relative from the webroot. # configure some static files } "^/assets/.+" => "$0", "^/favicon\.ico$" => "$0", $app = require __DIR__.'/../src/app.php'; $app->run(); For Apache 2.2.16+, you can use the FallbackResource "^(/[^\?]*)(\?.*)?" => "/index.php$1$2" directive: To start the server from the command-line: ) FallbackResource /index.php $ php -S localhost:8080 -t web web/index.php The application should be running at http://localhost:8080. App Configuration Global Configuration Stream To apply a controller setting to all controllers Streaming response (when you cannot buffer the data being sent) (a converter, a middleware, a requirement, or a default value), Default Configuration configure it on $app['controllers']: $app->stream($stream, 200, array('Content-Type' => 'image/png')); To send chunks, make sure to call ob_flush and flush after Configuration Default value $app['controllers'] applied to already registered every chunk: ->value('id', '1') controllers and become the $app['debug'] false ->assert('id', '\d+') defaults for new controllers $stream = function () { ->requireHttps() $app['request.http_port'] 80 $fh = fopen('http://www.example.com/', 'rb'); ->method('get') $app['request.https_port'] 443 ->convert('id', function () {/* ... */ }) while (!feof($fh)) { $app['charset'] ‘UTF-8’ ->before(function () {/* ... */ }); echo fread($fh, 1024); $app['locale'] ‘en’ ob_flush(); $app['logger'] null Global configuration does not apply to controller providers flush(); you might mount as they have their own global configuration } fclose($fh); Set Get }; $app['debug'] = true; $debug = $app['debug']; Symfony2 Components // turn on the debug mode Symfony2 components used by Silex: Abort HttpFoundation For Request and Response. Stop the request early. It actually throws an exception Custom Configuration (Parameters) HttpKernel Because we need a heart. $app->abort(404, "Book $id does not exist."); Routing For matching defined routes. Set EventDispatcher For hooking into the HttpKernel $app['asset_path'] = 'http://assets.examples.com'; HttpFoundation 2.2+ $app['base_url'] = '/'; Sending a file App Helper Methods Create a BinaryFileResponse Get $app->sendFile('/base/path/' . $path) $assetPath = $app['asset_path']; ->setContentDisposition( Redirect ResponseHeaderBag::DISPOSITION_ATTACHMENT, Using in a template (Twig) $app->redirect('/account'); 'pic.jpg') {{ app.asset_path }}/css/styles.css Forward $subRequest = Request::create('/hi', 'GET'); Escape $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST); Escape user input, to prevent Cross-Site-Scripting attacks If your application is hosted behind a reverse proxy at address $ip, and you want Silex to trust the Generate the URI using UrlGeneratorProvider: $app->escape($name) X-Forwarded-For* headers, you will need to run your $r = Request::create($app['url_generator']->generate('hi’), 'GET'); application like this: JSON use Symfony\Component\HttpFoundation\Request; App Events Return JSON data and apply correct escape Request::setTrustedProxies(array($ip)); $app->json($error, 404); EARLY_EVENT = 512; $app->run(); $app->json($user); LATE_EVENT = -512; Routing HTTP Methods HTTP method override (for PUT, DELETE, and PATCH) A route pattern consists of: POST $app->post('/books', function ($id) { // ... Defines a path that points to a resource. Forms in most web browsers do not directly support Pattern }); Can include variable parts and you are able the use of other HTTP methods. to set RegExp requirements for them Use a special form field named _method. GET $app->get('/books/{id}', function () { The form's method attribute must be set to POST Method One of the HTTP methods: // ... when using this field: GET, POST, PUT or DELETE. }); Describes the interaction with the resource <form action="/my/target/route/" method="POST"> <!-- ... --> PUT $app->put('/books/{id}', function ($id) { <input type="hidden" name="_method" value="PUT" /> // ... method pattern </form> }); $app->get('/blog', function () use ($posts) { $output = ''; DELETE $app->delete('/books/{id}', function ($id) { For Symfony Components 2.2+: // ... explicitly enable method override foreach ($posts as $post) { }); $output .= $post['title']; use Symfony\Component\HttpFoundation\Request; $output .= '<br />'; } PATCH $app->patch('/books/{id}', function ($id) { Request::enableHttpMethodParameterOverride(); // ... $app->run(); return $output; }); }); Matching all Methods $app->match('/book’, function () { Dynamic Route (Route Variable) Default Values // ... }); variable part passed to the closure To define a default value for any route variable call value on the $app->match('/book', function () { Controller object: // ... Can be restricted $app->get('/blog/{id}', function (Silex\Application $app, $id) use ($posts) { }) via the method ->method('PATCH'); method if (!isset($posts[$id])) { $app->get('/{page}', function ($page) { // ... $app->abort(404, "Post $id does not exist."); $app->match('/book', function () { } }) The current App is ->value('page’, 'index'); // ... automatically injected }) $post = $posts[$id]; to the closure thanks ->method('PUT|POST'); to the type hinting This will allow matching /, in which return "<h1>{$post['title']}</h1>" . case the page variable will have the The order of the routes is significant. “<p>{$post['body']}</p>"; value index }); The first matching route will be used (place more generic routes at the bottom) Route Variables Converters Converter defined as a service Requirements E.g.: user converter based on Doctrine ObjectManager: Appling some converters before injecting route variables $app->get('/blog/{id}', function ($id) { into the controller: use Doctrine\Common\Persistence\ObjectManager; // ... use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; }) $app->get('/user/{id}', function ($id) { ->assert('id', '\d+'); // ... class UserConverter { })->convert('id', function ($id) {return (int) $id; }); private $om; Chained requirements public function __construct(ObjectManager $om) { $app->get('/blog/{postId}/{commentId}', function ($postId, $commentId) { $this->om = $om; // ... Converting route variables to objects } }) ->assert('postId', '\d+') $userProvider = function ($id) { public function convert($id) ->assert('commentId', '\d+'); return new User($id); { }; if (null === $user = $this->om->find('User', (int) $id)) { throw new NotFoundHttpException( sprintf('User %d does not exist', $id) $app->get('/user/{user}/edit', function (User $user) { ); Named Routes // ... } })->convert('user', $userProvider); return $user; $app->get('/', function () { } // ... } }) The converter callback also receives the Request as ->bind('homepage'); its second argument: The service will now be registered in the app, and the convert method will be used as converter: $app->get('/blog/{id}', function ($id) { $callback = function ($post, Request $req) { // ... return new Post($req->attributes->get('slug')); $app['converter.user'] = $app->share(function () { }) }; return new UserConverter(); ->bind('blog_post'); }); $app->get('/blog/{id}/{slug}', function (Post $post) { It only makes sense to name routes if you use providers // ... $app->get('/user/{user}', function (User $user) { that make use of the RouteCollection })->convert('post', $callback); // ... version