├── .gitignore ├── LICENSE ├── README.md ├── bootstrap ├── app.php └── routes.php ├── composer.json ├── composer.lock ├── misc ├── .env.example ├── Dockerfile ├── Vagrantfile ├── config │ └── nginx │ │ ├── docker.conf │ │ └── vagrant.conf └── docker-compose.yml ├── public ├── assets │ └── css │ │ └── styles.css └── index.php ├── src └── Controllers │ ├── GreetController.php │ └── IndexController.php └── views ├── base.html.twig └── pages ├── greet.html.twig └── index.html.twig /.gitignore: -------------------------------------------------------------------------------- 1 | ### JetBrains template 2 | *.iml 3 | .idea/ 4 | *.ipr 5 | *.iws 6 | 7 | 8 | ### Composer template 9 | composer.phar 10 | vendor/ 11 | 12 | 13 | ### Vagrant template 14 | .vagrant/ 15 | 16 | 17 | ### Custom 18 | .env 19 | 20 | 21 | ### OSX template 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Icon must end with two \r 27 | Icon 28 | 29 | # Thumbnails 30 | ._* 31 | 32 | # Files that might appear in the root of a volume 33 | .DocumentRevisions-V100 34 | .fseventsd 35 | .Spotlight-V100 36 | .TemporaryItems 37 | .Trashes 38 | .VolumeIcon.icns 39 | 40 | # Directories potentially created on remote AFP share 41 | .AppleDB 42 | .AppleDesktop 43 | Network Trash Folder 44 | Temporary Items 45 | .apdisk 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frameworkless 2 | An admittedly overly simplistic example of combining a few popular packages into your own micro-framework. 3 | 4 | 5 | ## Why? 6 | 7 | **Education.** 8 | 9 | I do not recommend building your own framework unless you have a very compelling reason to do so. 10 | Instead,use a popular & well-supported framework like [Symfony](http://symfony.com), [Slim](https://www.slimframework.com), or [Laravel](http://laravel.com). 11 | 12 | #### What's included? 13 | I spent some time picking out packages, preferring those used by existing large applications or frameworks. Here is what's included, in no particular order: 14 | 15 | * **[nikic/fast-route](https://github.com/nikic/FastRoute)** 16 | * Popular routing library used by frameworks like [Slim](http://www.slimframework.com). 17 | * **[filp/whoops](https://github.com/filp/whoops)** 18 | * Stunning error handler, it makes errors sting a bit less. 19 | * **[symfony/http-foundation](https://github.com/symfony/http-foundation)** 20 | * Simplifies request & reponse handling. 21 | * **[league/container](https://github.com/thephpleague/container)** 22 | * Dependency injection container, for sharing common resources (like a database connection). 23 | * **[twig/twig](https://github.com/twigphp/Twig)** 24 | * The rock solid templating engine used by Symfony and many others. 25 | * **[vlucas/phpdotenv](https://github.com/vlucas/phpdotenv)** 26 | * Please don't push your database credentials to GitHub. 27 | 28 | 29 | ## Getting started with Vagrant 30 | 31 | #### Prerequisites 32 | * [Vagrant](https://www.vagrantup.com) 33 | * [VirtualBox](https://www.virtualbox.org) 34 | * [composer](https://getcomposer.org) 35 | 36 | #### Steps 37 | 1. `git clone` this repository 38 | 2. `cd` into the cloned repository 39 | 3. `composer install` 40 | 4. `cp misc/.env.example .env` 41 | 5. `cp misc/Vagrantfile .` 42 | 6. `vagrant up` 43 | 44 | From here, you should be able to access http://localhost:8080/. This website is served using NGINX & PHP 7. 45 | 46 | 47 | ## Getting started with Docker 48 | 49 | #### Prerequisites 50 | * [Docker](https://www.docker.com) 51 | * [Docker Compose](https://docs.docker.com/compose) 52 | * [composer](https://getcomposer.org) 53 | 54 | #### Steps 55 | 1. `git clone` this repository 56 | 2. `cd` into the cloned repository 57 | 3. `composer install` 58 | 4. `cp misc/.env.example .env` 59 | 5. `cp misc/Dockerfile .` 60 | 6. `cp misc/docker-compose.yml .` 61 | 7. `docker-compose up` 62 | 63 | Run `docker-compose port application 80` to find the public port this application is running on. 64 | 65 | 66 | ## Batteries not included 67 | I've intentionally made this project as simplistic as possible. A lot of things are left up to you to design and implement. On the plus side, you won't have to remove much boilerplate. 68 | 69 | Below you will find instructions on how to implement a few things, feel free to contribute more examples :). 70 | 71 | ### PDO (database) 72 | Edit `bootstrap/app.php` and add the following: 73 | ```php 74 | $container 75 | ->add('PDO') 76 | ->withArgument(getenv('DB_CONN')) 77 | ->withArgument(getenv('DB_USER')) 78 | ->withArgument(getenv('DB_PASS')); 79 | ``` 80 | 81 | You will also need to add some values to your `.env` 82 | ``` 83 | # Database access 84 | DB_CONN=mysql:host=127.0.0.1;dbname=frameworkless;charset=utf8 85 | DB_USER=fwl_user 86 | DB_PASS=hopefullysecure 87 | ``` 88 | 89 | Now, from a controller: 90 | ```php 91 | private $pdo; 92 | 93 | public function __construct(PDO $pdo) 94 | { 95 | $this->pdo = $pdo; 96 | } 97 | 98 | public function get() 99 | { 100 | $handle = $this->pdo->prepare('SELECT * FROM `todos`'); 101 | $handle->execute(); 102 | return new JsonResponse($handle->fetchAll(PDO::FETCH_ASSOC)); 103 | } 104 | ``` 105 | 106 | ### Spot (database, ORM) 107 | ``` 108 | composer require vlucas/spot2 109 | ``` 110 | 111 | from here, edit `bootstrap/app.php` and add the following: 112 | ```php 113 | $db = new \Spot\Config(); 114 | $db->addConnection('mysql', [ 115 | 'dbname' => getenv('DB_NAME'), 116 | 'user' => getenv('DB_USER'), 117 | 'password' => getenv('DB_PASS'), 118 | 'host' => getenv('DB_HOST') 119 | ]); 120 | 121 | $container 122 | ->add('\Spot\Locator') 123 | ->withArgument($db); 124 | ``` 125 | 126 | You will also need to add some values to your `.env` 127 | ``` 128 | # Database access 129 | DB_CONN=mysql:host=127.0.0.1;dbname=frameworkless;charset=utf8 130 | DB_USER=fwl_user 131 | DB_PASS=hopefullysecure 132 | ``` 133 | 134 | Now you can create models! I recommend adding them under a src/Models directory for separation. For example, `src/Models/Posts.php`: 135 | ```php 136 | namespace Frameworkless\Models; 137 | 138 | use Spot\Entity; 139 | 140 | class Posts extends Entity 141 | { 142 | protected static $table = 'posts'; 143 | // etc. 144 | } 145 | ``` 146 | 147 | And finally from your controller: 148 | ```php 149 | private $spot; 150 | 151 | public function __construct(\Spot\Locator $spot) 152 | { 153 | $this->spot = $spot; 154 | } 155 | 156 | public function get() 157 | { 158 | $posts = $this->spot->mapper('Frameworkless\Models\Posts')->all(); 159 | return new Response('Here are your posts ' . print_r($posts, true)); 160 | } 161 | ``` 162 | 163 | 164 | ## Contributing 165 | Submit a pull request :) I'll be friendly 166 | 167 | 168 | Thanks to **@waxim** for contributing the Spot example 169 | 170 | Thanks to **@jaakkytt** for clearing up part of this readme 171 | 172 | Thanks to **@Luciam91** for contributing Docker support 173 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | prepare($request) 26 | ->send(); 27 | return; 28 | } 29 | $dotenv = new Dotenv\Dotenv(__DIR__ . '/../'); 30 | $dotenv->load(); 31 | 32 | 33 | /* 34 | * Error handler 35 | */ 36 | $whoops = new Run; 37 | if (getenv('MODE') === 'dev') { 38 | $whoops->pushHandler( 39 | new PrettyPageHandler() 40 | ); 41 | } else { 42 | $whoops->pushHandler( 43 | // Using the pretty error handler in production is likely a bad idea. 44 | // Instead respond with a generic error message. 45 | function () use ($request) { 46 | Response::create('An internal server error has occurred.', Response::HTTP_INTERNAL_SERVER_ERROR) 47 | ->prepare($request) 48 | ->send(); 49 | } 50 | ); 51 | } 52 | $whoops->register(); 53 | 54 | 55 | /* 56 | * Container setup 57 | */ 58 | $container = new Container(); 59 | $container 60 | ->add('Twig_Environment') 61 | ->withArgument( 62 | // Our twig templates are stored inside of the views directory. 63 | new Twig_Loader_Filesystem(__DIR__ . '/../views/') 64 | ); 65 | $container 66 | ->delegate( 67 | // Auto-wiring based on constructor typehints. 68 | // http://container.thephpleague.com/auto-wiring 69 | new ReflectionContainer() 70 | ); 71 | 72 | 73 | /* 74 | * Routes 75 | */ 76 | $dispatcher = FastRoute\simpleDispatcher(function (FastRoute\RouteCollector $r) { 77 | $routes = require __DIR__ . '/routes.php'; 78 | foreach ($routes as $route) { 79 | $r->addRoute($route[0], $route[1], $route[2]); 80 | } 81 | }); 82 | 83 | 84 | /* 85 | * Dispatch 86 | */ 87 | $routeInfo = $dispatcher->dispatch($request->getMethod(), $request->getPathInfo()); 88 | switch ($routeInfo[0]) { 89 | case Dispatcher::NOT_FOUND: 90 | // No matching route was found. 91 | Response::create("404 Not Found", Response::HTTP_NOT_FOUND) 92 | ->prepare($request) 93 | ->send(); 94 | break; 95 | case Dispatcher::METHOD_NOT_ALLOWED: 96 | // A matching route was found, but the wrong HTTP method was used. 97 | Response::create("405 Method Not Allowed", Response::HTTP_METHOD_NOT_ALLOWED) 98 | ->prepare($request) 99 | ->send(); 100 | break; 101 | case Dispatcher::FOUND: 102 | // Fully qualified class name of the controller 103 | $fqcn = $routeInfo[1][0]; 104 | // Controller method responsible for handling the request 105 | $routeMethod = $routeInfo[1][1]; 106 | // Route parameters (ex. /products/{category}/{id}) 107 | $routeParams = $routeInfo[2]; 108 | 109 | // Obtain an instance of route's controller 110 | // Resolves constructor dependencies using the container 111 | $controller = $container->get($fqcn); 112 | 113 | // Generate a response by invoking the appropriate route method in the controller 114 | $response = $controller->$routeMethod($routeParams); 115 | if ($response instanceof Response) { 116 | // Send the generated response back to the user 117 | $response 118 | ->prepare($request) 119 | ->send(); 120 | } 121 | break; 122 | default: 123 | // According to the dispatch(..) method's documentation this shouldn't happen. 124 | // But it's here anyways just to cover all of our bases. 125 | Response::create('Received unexpected response from dispatcher.', Response::HTTP_INTERNAL_SERVER_ERROR) 126 | ->prepare($request) 127 | ->send(); 128 | return; 129 | } 130 | -------------------------------------------------------------------------------- /bootstrap/routes.php: -------------------------------------------------------------------------------- 1 | =5.4.0" 114 | }, 115 | "provide": { 116 | "container-interop/container-interop-implementation": "^1.1" 117 | }, 118 | "replace": { 119 | "orno/di": "~2.0" 120 | }, 121 | "require-dev": { 122 | "phpunit/phpunit": "4.*" 123 | }, 124 | "type": "library", 125 | "extra": { 126 | "branch-alias": { 127 | "dev-master": "2.x-dev", 128 | "dev-1.x": "1.x-dev" 129 | } 130 | }, 131 | "autoload": { 132 | "psr-4": { 133 | "League\\Container\\": "src" 134 | } 135 | }, 136 | "notification-url": "https://packagist.org/downloads/", 137 | "license": [ 138 | "MIT" 139 | ], 140 | "authors": [ 141 | { 142 | "name": "Phil Bennett", 143 | "email": "philipobenito@gmail.com", 144 | "homepage": "http://www.philipobenito.com", 145 | "role": "Developer" 146 | } 147 | ], 148 | "description": "A fast and intuitive dependency injection container.", 149 | "homepage": "https://github.com/thephpleague/container", 150 | "keywords": [ 151 | "container", 152 | "dependency", 153 | "di", 154 | "injection", 155 | "league", 156 | "provider", 157 | "service" 158 | ], 159 | "time": "2016-03-17 11:07:59" 160 | }, 161 | { 162 | "name": "nikic/fast-route", 163 | "version": "v1.0.1", 164 | "source": { 165 | "type": "git", 166 | "url": "https://github.com/nikic/FastRoute.git", 167 | "reference": "8ea928195fa9b907f0d6e48312d323c1a13cc2af" 168 | }, 169 | "dist": { 170 | "type": "zip", 171 | "url": "https://api.github.com/repos/nikic/FastRoute/zipball/8ea928195fa9b907f0d6e48312d323c1a13cc2af", 172 | "reference": "8ea928195fa9b907f0d6e48312d323c1a13cc2af", 173 | "shasum": "" 174 | }, 175 | "require": { 176 | "php": ">=5.4.0" 177 | }, 178 | "type": "library", 179 | "autoload": { 180 | "psr-4": { 181 | "FastRoute\\": "src/" 182 | }, 183 | "files": [ 184 | "src/functions.php" 185 | ] 186 | }, 187 | "notification-url": "https://packagist.org/downloads/", 188 | "license": [ 189 | "BSD-3-Clause" 190 | ], 191 | "authors": [ 192 | { 193 | "name": "Nikita Popov", 194 | "email": "nikic@php.net" 195 | } 196 | ], 197 | "description": "Fast request router for PHP", 198 | "keywords": [ 199 | "router", 200 | "routing" 201 | ], 202 | "time": "2016-06-12 19:08:51" 203 | }, 204 | { 205 | "name": "symfony/http-foundation", 206 | "version": "v3.2.0", 207 | "source": { 208 | "type": "git", 209 | "url": "https://github.com/symfony/http-foundation.git", 210 | "reference": "9963bc29d7f4398b137dd8efc480efe54fdbe5f1" 211 | }, 212 | "dist": { 213 | "type": "zip", 214 | "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9963bc29d7f4398b137dd8efc480efe54fdbe5f1", 215 | "reference": "9963bc29d7f4398b137dd8efc480efe54fdbe5f1", 216 | "shasum": "" 217 | }, 218 | "require": { 219 | "php": ">=5.5.9", 220 | "symfony/polyfill-mbstring": "~1.1" 221 | }, 222 | "require-dev": { 223 | "symfony/expression-language": "~2.8|~3.0" 224 | }, 225 | "type": "library", 226 | "extra": { 227 | "branch-alias": { 228 | "dev-master": "3.2-dev" 229 | } 230 | }, 231 | "autoload": { 232 | "psr-4": { 233 | "Symfony\\Component\\HttpFoundation\\": "" 234 | }, 235 | "exclude-from-classmap": [ 236 | "/Tests/" 237 | ] 238 | }, 239 | "notification-url": "https://packagist.org/downloads/", 240 | "license": [ 241 | "MIT" 242 | ], 243 | "authors": [ 244 | { 245 | "name": "Fabien Potencier", 246 | "email": "fabien@symfony.com" 247 | }, 248 | { 249 | "name": "Symfony Community", 250 | "homepage": "https://symfony.com/contributors" 251 | } 252 | ], 253 | "description": "Symfony HttpFoundation Component", 254 | "homepage": "https://symfony.com", 255 | "time": "2016-11-27 04:21:38" 256 | }, 257 | { 258 | "name": "symfony/polyfill-mbstring", 259 | "version": "v1.3.0", 260 | "source": { 261 | "type": "git", 262 | "url": "https://github.com/symfony/polyfill-mbstring.git", 263 | "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" 264 | }, 265 | "dist": { 266 | "type": "zip", 267 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", 268 | "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", 269 | "shasum": "" 270 | }, 271 | "require": { 272 | "php": ">=5.3.3" 273 | }, 274 | "suggest": { 275 | "ext-mbstring": "For best performance" 276 | }, 277 | "type": "library", 278 | "extra": { 279 | "branch-alias": { 280 | "dev-master": "1.3-dev" 281 | } 282 | }, 283 | "autoload": { 284 | "psr-4": { 285 | "Symfony\\Polyfill\\Mbstring\\": "" 286 | }, 287 | "files": [ 288 | "bootstrap.php" 289 | ] 290 | }, 291 | "notification-url": "https://packagist.org/downloads/", 292 | "license": [ 293 | "MIT" 294 | ], 295 | "authors": [ 296 | { 297 | "name": "Nicolas Grekas", 298 | "email": "p@tchwork.com" 299 | }, 300 | { 301 | "name": "Symfony Community", 302 | "homepage": "https://symfony.com/contributors" 303 | } 304 | ], 305 | "description": "Symfony polyfill for the Mbstring extension", 306 | "homepage": "https://symfony.com", 307 | "keywords": [ 308 | "compatibility", 309 | "mbstring", 310 | "polyfill", 311 | "portable", 312 | "shim" 313 | ], 314 | "time": "2016-11-14 01:06:16" 315 | }, 316 | { 317 | "name": "symfony/var-dumper", 318 | "version": "v3.2.0", 319 | "source": { 320 | "type": "git", 321 | "url": "https://github.com/symfony/var-dumper.git", 322 | "reference": "86f4e8aeb07bd5fb467f6bdd599a30298d19fa5f" 323 | }, 324 | "dist": { 325 | "type": "zip", 326 | "url": "https://api.github.com/repos/symfony/var-dumper/zipball/86f4e8aeb07bd5fb467f6bdd599a30298d19fa5f", 327 | "reference": "86f4e8aeb07bd5fb467f6bdd599a30298d19fa5f", 328 | "shasum": "" 329 | }, 330 | "require": { 331 | "php": ">=5.5.9", 332 | "symfony/polyfill-mbstring": "~1.0" 333 | }, 334 | "require-dev": { 335 | "twig/twig": "~1.20|~2.0" 336 | }, 337 | "suggest": { 338 | "ext-symfony_debug": "" 339 | }, 340 | "type": "library", 341 | "extra": { 342 | "branch-alias": { 343 | "dev-master": "3.2-dev" 344 | } 345 | }, 346 | "autoload": { 347 | "files": [ 348 | "Resources/functions/dump.php" 349 | ], 350 | "psr-4": { 351 | "Symfony\\Component\\VarDumper\\": "" 352 | }, 353 | "exclude-from-classmap": [ 354 | "/Tests/" 355 | ] 356 | }, 357 | "notification-url": "https://packagist.org/downloads/", 358 | "license": [ 359 | "MIT" 360 | ], 361 | "authors": [ 362 | { 363 | "name": "Nicolas Grekas", 364 | "email": "p@tchwork.com" 365 | }, 366 | { 367 | "name": "Symfony Community", 368 | "homepage": "https://symfony.com/contributors" 369 | } 370 | ], 371 | "description": "Symfony mechanism for exploring and dumping PHP variables", 372 | "homepage": "https://symfony.com", 373 | "keywords": [ 374 | "debug", 375 | "dump" 376 | ], 377 | "time": "2016-11-29 10:33:09" 378 | }, 379 | { 380 | "name": "twig/twig", 381 | "version": "v1.28.2", 382 | "source": { 383 | "type": "git", 384 | "url": "https://github.com/twigphp/Twig.git", 385 | "reference": "b22ce0eb070e41f7cba65d78fe216de29726459c" 386 | }, 387 | "dist": { 388 | "type": "zip", 389 | "url": "https://api.github.com/repos/twigphp/Twig/zipball/b22ce0eb070e41f7cba65d78fe216de29726459c", 390 | "reference": "b22ce0eb070e41f7cba65d78fe216de29726459c", 391 | "shasum": "" 392 | }, 393 | "require": { 394 | "php": ">=5.2.7" 395 | }, 396 | "require-dev": { 397 | "symfony/debug": "~2.7", 398 | "symfony/phpunit-bridge": "~3.2@dev" 399 | }, 400 | "type": "library", 401 | "extra": { 402 | "branch-alias": { 403 | "dev-master": "1.28-dev" 404 | } 405 | }, 406 | "autoload": { 407 | "psr-0": { 408 | "Twig_": "lib/" 409 | } 410 | }, 411 | "notification-url": "https://packagist.org/downloads/", 412 | "license": [ 413 | "BSD-3-Clause" 414 | ], 415 | "authors": [ 416 | { 417 | "name": "Fabien Potencier", 418 | "email": "fabien@symfony.com", 419 | "homepage": "http://fabien.potencier.org", 420 | "role": "Lead Developer" 421 | }, 422 | { 423 | "name": "Armin Ronacher", 424 | "email": "armin.ronacher@active-4.com", 425 | "role": "Project Founder" 426 | }, 427 | { 428 | "name": "Twig Team", 429 | "homepage": "http://twig.sensiolabs.org/contributors", 430 | "role": "Contributors" 431 | } 432 | ], 433 | "description": "Twig, the flexible, fast, and secure template language for PHP", 434 | "homepage": "http://twig.sensiolabs.org", 435 | "keywords": [ 436 | "templating" 437 | ], 438 | "time": "2016-11-23 18:41:40" 439 | }, 440 | { 441 | "name": "vlucas/phpdotenv", 442 | "version": "v2.4.0", 443 | "source": { 444 | "type": "git", 445 | "url": "https://github.com/vlucas/phpdotenv.git", 446 | "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" 447 | }, 448 | "dist": { 449 | "type": "zip", 450 | "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", 451 | "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", 452 | "shasum": "" 453 | }, 454 | "require": { 455 | "php": ">=5.3.9" 456 | }, 457 | "require-dev": { 458 | "phpunit/phpunit": "^4.8 || ^5.0" 459 | }, 460 | "type": "library", 461 | "extra": { 462 | "branch-alias": { 463 | "dev-master": "2.4-dev" 464 | } 465 | }, 466 | "autoload": { 467 | "psr-4": { 468 | "Dotenv\\": "src/" 469 | } 470 | }, 471 | "notification-url": "https://packagist.org/downloads/", 472 | "license": [ 473 | "BSD-3-Clause-Attribution" 474 | ], 475 | "authors": [ 476 | { 477 | "name": "Vance Lucas", 478 | "email": "vance@vancelucas.com", 479 | "homepage": "http://www.vancelucas.com" 480 | } 481 | ], 482 | "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", 483 | "keywords": [ 484 | "dotenv", 485 | "env", 486 | "environment" 487 | ], 488 | "time": "2016-09-01 10:05:43" 489 | } 490 | ], 491 | "packages-dev": [], 492 | "aliases": [], 493 | "minimum-stability": "stable", 494 | "stability-flags": [], 495 | "prefer-stable": false, 496 | "prefer-lowest": false, 497 | "platform": [], 498 | "platform-dev": [] 499 | } 500 | -------------------------------------------------------------------------------- /misc/.env.example: -------------------------------------------------------------------------------- 1 | # If this is set to dev, an error handler will be shown. Not good for production. 2 | MODE=dev 3 | 4 | # Note: Instead of hard coding credentials and pushing them to GitHub, 5 | # put them in this file (.env, *not* in your .env.example) which is .gitignore'd. 6 | -------------------------------------------------------------------------------- /misc/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7-fpm 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y nginx \ 5 | && apt-get clean && apt-get purge \ 6 | && rm -rf /var/lib/apt/lists/* /var/cache/apt/* 7 | 8 | EXPOSE 80 443 9 | 10 | WORKDIR /opt/frameworkless/public 11 | 12 | ENTRYPOINT /usr/local/sbin/php-fpm -D && /usr/sbin/nginx -g 'daemon off;' -------------------------------------------------------------------------------- /misc/Vagrantfile: -------------------------------------------------------------------------------- 1 | $script = <<'SCRIPT' 2 | #!/usr/bin/env bash 3 | # ################################################### # 4 | # ! Frameworkless: Initial setup for Ubuntu 16.04 ! # 5 | # ################################################### # 6 | 7 | # Install NGINX & PHP 7 8 | sudo apt-get update 9 | sudo apt-get install -y nginx php7.0-fpm 10 | 11 | # Symlink the nginx config and restart nginx 12 | sudo rm -f /etc/nginx/sites-available/* 13 | sudo rm -f /etc/nginx/sites-enabled/* 14 | sudo ln -s /opt/frameworkless/misc/config/nginx/vagrant.conf /etc/nginx/sites-enabled/default 15 | sudo systemctl restart nginx 16 | SCRIPT 17 | 18 | Vagrant.configure(2) do |config| 19 | config.vm.box = "ubuntu/xenial64" 20 | config.vm.network "forwarded_port", guest: 80, host: 8080 21 | 22 | config.vm.synced_folder ".", "/vagrant", disabled: true 23 | config.vm.synced_folder ".", "/opt/frameworkless" 24 | 25 | config.ssh.insert_key = true 26 | 27 | config.vm.provision "shell", inline: $script 28 | end 29 | -------------------------------------------------------------------------------- /misc/config/nginx/docker.conf: -------------------------------------------------------------------------------- 1 | server { 2 | root /opt/frameworkless/public; 3 | index index.php; 4 | 5 | # Disable cache (FOR DEVELOPMENT SERVER ONLY) 6 | expires off; 7 | sendfile off; 8 | 9 | # Try the requested file first, otherwise use the front controller 10 | location / { 11 | try_files $uri /index.php?$query_string; 12 | } 13 | 14 | # Interpret PHP 15 | location ~ \.php$ { 16 | try_files $uri =404; 17 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 18 | fastcgi_pass 127.0.0.1:9000; 19 | fastcgi_index index.php; 20 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 21 | include fastcgi_params; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /misc/config/nginx/vagrant.conf: -------------------------------------------------------------------------------- 1 | server { 2 | root /opt/frameworkless/public; 3 | index index.php; 4 | 5 | # Disable cache (FOR DEVELOPMENT SERVER ONLY) 6 | expires off; 7 | sendfile off; 8 | 9 | # Try the requested file first, otherwise use the front controller 10 | location / { 11 | try_files $uri /index.php?$query_string; 12 | } 13 | 14 | # Interpret PHP 15 | location ~ \.php$ { 16 | try_files $uri =404; 17 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 18 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock; 19 | fastcgi_index index.php; 20 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 21 | include fastcgi_params; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /misc/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | application: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | volumes: 9 | - ./misc/config/nginx/docker.conf:/etc/nginx/sites-enabled/default 10 | - .:/opt/frameworkless 11 | ports: 12 | - 80 13 | -------------------------------------------------------------------------------- /public/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | Original CSS taken from http://evenbettermotherfucking.website 3 | */ 4 | 5 | body { 6 | margin: 2% auto; 7 | background: #f2f2f2; 8 | color: #444444; 9 | font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; 10 | font-size: 14px; 11 | line-height: 1.6; 12 | text-shadow: 0 1px 0 #ffffff; 13 | min-width: 768px; 14 | max-width: 50%; 15 | padding: 1em; 16 | } 17 | 18 | code { 19 | background: white; 20 | } 21 | 22 | a { 23 | border-bottom: 1px solid #444444; 24 | color: #444444; 25 | text-decoration: none; 26 | } 27 | 28 | a:hover { 29 | border-bottom: 0; 30 | } 31 | 32 | h1, h2 { 33 | line-height: 1.3; 34 | } 35 | ul li { 36 | padding-bottom: 5px; 37 | } -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class GreetController 13 | { 14 | /** @var Twig_Environment */ 15 | private $twig; 16 | 17 | /** 18 | * GreetController, constructed by the container 19 | * 20 | * @param Twig_Environment $twig 21 | */ 22 | public function __construct(Twig_Environment $twig) 23 | { 24 | $this->twig = $twig; 25 | } 26 | 27 | /** 28 | * Name reversing thing 29 | * 30 | * @param array $routeParams 31 | * @return Response 32 | */ 33 | public function greet($routeParams) 34 | { 35 | $response = new Response( 36 | $this->twig->render('pages/greet.html.twig', [ 37 | 'name' => strrev($routeParams['name']) 38 | ]) 39 | ); 40 | return $response; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Controllers/IndexController.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class IndexController 14 | { 15 | /** @var Twig_Environment */ 16 | private $twig; 17 | 18 | /** 19 | * IndexController, constructed by the container 20 | * 21 | * @param Twig_Environment $twig 22 | */ 23 | public function __construct(Twig_Environment $twig) 24 | { 25 | $this->twig = $twig; 26 | } 27 | 28 | /** 29 | * Index page 30 | * 31 | * @return Response 32 | */ 33 | public function index() 34 | { 35 | return new Response($this->twig->render('pages/index.html.twig')); 36 | } 37 | 38 | /** 39 | * Throw an exception (for testing the error handler) 40 | * 41 | * @throws Exception 42 | */ 43 | public function exception() 44 | { 45 | throw new Exception('Test exception'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /views/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block title %}Default title{% endblock %} 5 | 6 | 7 | 8 | {% block content %}Default content{% endblock %} 9 | 10 | 11 | -------------------------------------------------------------------------------- /views/pages/greet.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block title %} 4 | Name 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |

Hello {{ name }}!

10 |

I really need to create a better test page.

11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /views/pages/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block title %} 4 | Welcome 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |

Yay, it works!

10 |
11 |

Example pages

12 | 20 |
21 | {% endblock %} 22 | --------------------------------------------------------------------------------