├── .gitignore ├── Dockerfile ├── README.md ├── composer.json ├── composer.lock ├── continuous-pipe.yml ├── docker-compose.yml ├── docker ├── apache │ ├── run.sh │ ├── start_safe_perms │ └── vhost.conf └── php │ └── php.ini ├── src └── App │ └── Controller │ └── StatusController.php └── web └── index.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7-apache 2 | 3 | MAINTAINER Samuel ROZE 4 | 5 | # PHP extension 6 | RUN requirements="zlib1g-dev libicu-dev git curl" \ 7 | && apt-get update && apt-get install -y $requirements && rm -rf /var/lib/apt/lists/* \ 8 | && docker-php-ext-install pdo_mysql \ 9 | && docker-php-ext-install intl \ 10 | && docker-php-ext-install zip \ 11 | && apt-get purge --auto-remove -y 12 | 13 | # Apache & PHP configuration 14 | RUN a2enmod rewrite 15 | ADD docker/apache/vhost.conf /etc/apache2/sites-enabled/000-default.conf 16 | ADD docker/php/php.ini /usr/local/etc/php/php.ini 17 | 18 | # Install composer 19 | RUN curl -sS https://getcomposer.org/installer | php \ 20 | && mv composer.phar /usr/bin/composer 21 | 22 | # Add the application 23 | ADD . /app 24 | WORKDIR /app 25 | 26 | # Install dependencies 27 | RUN composer install -o 28 | 29 | # Ensure that the production container will run with the www-data user 30 | RUN chown www-data /app 31 | 32 | CMD ["/app/docker/apache/run.sh"] 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP example application 2 | 3 | This is a Silex application that is used as demonstration purpose to deploy Docker-based applications. 4 | 5 | ## Start locally 6 | 7 | In order to have useful feature such as the DNS resolution, you can start the application with [dock-cli](https://github.com/inviqa/dock-cli). 8 | 9 | ``` 10 | dock-cli start 11 | ``` 12 | 13 | *Note*: you can also start the application with `docker-compose`. 14 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "php": ">=5.5.0", 4 | "silex/silex": "~1.1", 5 | "doctrine/dbal": "2.2.*" 6 | }, 7 | "autoload": { 8 | "psr-0": { "": "src/" } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "2d58a470abbc52a056f1ae869fee021b", 8 | "packages": [ 9 | { 10 | "name": "doctrine/common", 11 | "version": "2.2.3", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/doctrine/common.git", 15 | "reference": "9e13facbacb001d324d71a0f6d25d8d7f12346b6" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/doctrine/common/zipball/9e13facbacb001d324d71a0f6d25d8d7f12346b6", 20 | "reference": "9e13facbacb001d324d71a0f6d25d8d7f12346b6", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3.2" 25 | }, 26 | "type": "library", 27 | "autoload": { 28 | "psr-0": { 29 | "Doctrine\\Common": "lib/" 30 | } 31 | }, 32 | "notification-url": "https://packagist.org/downloads/", 33 | "license": [ 34 | "LGPL" 35 | ], 36 | "authors": [ 37 | { 38 | "name": "Jonathan Wage", 39 | "email": "jonwage@gmail.com", 40 | "homepage": "http://www.jwage.com/", 41 | "role": "Creator" 42 | }, 43 | { 44 | "name": "Guilherme Blanco", 45 | "email": "guilhermeblanco@gmail.com", 46 | "homepage": "http://www.instaclick.com" 47 | }, 48 | { 49 | "name": "Roman Borschel", 50 | "email": "roman@code-factory.org" 51 | }, 52 | { 53 | "name": "Benjamin Eberlei", 54 | "email": "kontakt@beberlei.de" 55 | }, 56 | { 57 | "name": "Johannes Schmitt", 58 | "email": "schmittjoh@gmail.com", 59 | "homepage": "https://github.com/schmittjoh", 60 | "role": "Developer of wrapped JMSSerializerBundle" 61 | } 62 | ], 63 | "description": "Common Library for Doctrine projects", 64 | "homepage": "http://www.doctrine-project.org", 65 | "keywords": [ 66 | "annotations", 67 | "collections", 68 | "eventmanager", 69 | "persistence", 70 | "spl" 71 | ], 72 | "time": "2012-08-29 08:04:14" 73 | }, 74 | { 75 | "name": "doctrine/dbal", 76 | "version": "2.2.2", 77 | "source": { 78 | "type": "git", 79 | "url": "https://github.com/doctrine/dbal.git", 80 | "reference": "ee8f64111d03df2dbf3c106214514fce4a9857ab" 81 | }, 82 | "dist": { 83 | "type": "zip", 84 | "url": "https://api.github.com/repos/doctrine/dbal/zipball/ee8f64111d03df2dbf3c106214514fce4a9857ab", 85 | "reference": "ee8f64111d03df2dbf3c106214514fce4a9857ab", 86 | "shasum": "" 87 | }, 88 | "require": { 89 | "doctrine/common": ">=2.2.0,<=2.2.99", 90 | "php": ">=5.3.2" 91 | }, 92 | "type": "library", 93 | "autoload": { 94 | "psr-0": { 95 | "Doctrine\\DBAL": "lib/" 96 | } 97 | }, 98 | "notification-url": "https://packagist.org/downloads/", 99 | "license": [ 100 | "LGPL" 101 | ], 102 | "authors": [ 103 | { 104 | "name": "Jonathan Wage", 105 | "email": "jonwage@gmail.com", 106 | "homepage": "http://www.jwage.com/", 107 | "role": "Creator" 108 | }, 109 | { 110 | "name": "Guilherme Blanco", 111 | "email": "guilhermeblanco@gmail.com", 112 | "homepage": "http://www.instaclick.com" 113 | }, 114 | { 115 | "name": "Roman Borschel", 116 | "email": "roman@code-factory.org" 117 | }, 118 | { 119 | "name": "Benjamin Eberlei", 120 | "email": "kontakt@beberlei.de" 121 | } 122 | ], 123 | "description": "Database Abstraction Layer", 124 | "homepage": "http://www.doctrine-project.org", 125 | "keywords": [ 126 | "database", 127 | "dbal", 128 | "persistence", 129 | "queryobject" 130 | ], 131 | "time": "2012-04-13 07:56:12" 132 | }, 133 | { 134 | "name": "pimple/pimple", 135 | "version": "v1.1.1", 136 | "source": { 137 | "type": "git", 138 | "url": "https://github.com/silexphp/Pimple.git", 139 | "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" 140 | }, 141 | "dist": { 142 | "type": "zip", 143 | "url": "https://api.github.com/repos/silexphp/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", 144 | "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", 145 | "shasum": "" 146 | }, 147 | "require": { 148 | "php": ">=5.3.0" 149 | }, 150 | "type": "library", 151 | "extra": { 152 | "branch-alias": { 153 | "dev-master": "1.1.x-dev" 154 | } 155 | }, 156 | "autoload": { 157 | "psr-0": { 158 | "Pimple": "lib/" 159 | } 160 | }, 161 | "notification-url": "https://packagist.org/downloads/", 162 | "license": [ 163 | "MIT" 164 | ], 165 | "authors": [ 166 | { 167 | "name": "Fabien Potencier", 168 | "email": "fabien@symfony.com" 169 | } 170 | ], 171 | "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", 172 | "homepage": "http://pimple.sensiolabs.org", 173 | "keywords": [ 174 | "container", 175 | "dependency injection" 176 | ], 177 | "time": "2013-11-22 08:30:29" 178 | }, 179 | { 180 | "name": "psr/log", 181 | "version": "1.0.0", 182 | "source": { 183 | "type": "git", 184 | "url": "https://github.com/php-fig/log.git", 185 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" 186 | }, 187 | "dist": { 188 | "type": "zip", 189 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", 190 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", 191 | "shasum": "" 192 | }, 193 | "type": "library", 194 | "autoload": { 195 | "psr-0": { 196 | "Psr\\Log\\": "" 197 | } 198 | }, 199 | "notification-url": "https://packagist.org/downloads/", 200 | "license": [ 201 | "MIT" 202 | ], 203 | "authors": [ 204 | { 205 | "name": "PHP-FIG", 206 | "homepage": "http://www.php-fig.org/" 207 | } 208 | ], 209 | "description": "Common interface for logging libraries", 210 | "keywords": [ 211 | "log", 212 | "psr", 213 | "psr-3" 214 | ], 215 | "time": "2012-12-21 11:40:51" 216 | }, 217 | { 218 | "name": "silex/silex", 219 | "version": "v1.2.2", 220 | "source": { 221 | "type": "git", 222 | "url": "https://github.com/silexphp/Silex.git", 223 | "reference": "8c5e86eb97f3eee633729b22e950082fb5591328" 224 | }, 225 | "dist": { 226 | "type": "zip", 227 | "url": "https://api.github.com/repos/silexphp/Silex/zipball/8c5e86eb97f3eee633729b22e950082fb5591328", 228 | "reference": "8c5e86eb97f3eee633729b22e950082fb5591328", 229 | "shasum": "" 230 | }, 231 | "require": { 232 | "php": ">=5.3.3", 233 | "pimple/pimple": "~1.0", 234 | "symfony/event-dispatcher": ">=2.3,<2.6-dev", 235 | "symfony/http-foundation": ">=2.3,<2.6-dev", 236 | "symfony/http-kernel": ">=2.3,<2.6-dev", 237 | "symfony/routing": ">=2.3,<2.6-dev" 238 | }, 239 | "require-dev": { 240 | "doctrine/dbal": "~2.2", 241 | "monolog/monolog": "~1.4,>=1.4.1", 242 | "phpunit/phpunit": "~3.7", 243 | "swiftmailer/swiftmailer": "5.*", 244 | "symfony/browser-kit": ">=2.3,<2.6-dev", 245 | "symfony/config": ">=2.3,<2.6-dev", 246 | "symfony/css-selector": ">=2.3,<2.6-dev", 247 | "symfony/debug": ">=2.3,<2.6-dev", 248 | "symfony/dom-crawler": ">=2.3,<2.6-dev", 249 | "symfony/finder": ">=2.3,<2.6-dev", 250 | "symfony/form": ">=2.3,<2.6-dev", 251 | "symfony/locale": ">=2.3,<2.6-dev", 252 | "symfony/monolog-bridge": ">=2.3,<2.6-dev", 253 | "symfony/options-resolver": ">=2.3,<2.6-dev", 254 | "symfony/process": ">=2.3,<2.6-dev", 255 | "symfony/security": ">=2.3,<2.6-dev", 256 | "symfony/serializer": ">=2.3,<2.6-dev", 257 | "symfony/translation": ">=2.3,<2.6-dev", 258 | "symfony/twig-bridge": ">=2.3,<2.6-dev", 259 | "symfony/validator": ">=2.3,<2.6-dev", 260 | "twig/twig": ">=1.8.0,<2.0-dev" 261 | }, 262 | "suggest": { 263 | "symfony/browser-kit": ">=2.3,<2.6-dev", 264 | "symfony/css-selector": ">=2.3,<2.6-dev", 265 | "symfony/dom-crawler": ">=2.3,<2.6-dev", 266 | "symfony/form": ">=2.3,<2.6-dev" 267 | }, 268 | "type": "library", 269 | "extra": { 270 | "branch-alias": { 271 | "dev-master": "1.2.x-dev" 272 | } 273 | }, 274 | "autoload": { 275 | "psr-0": { 276 | "Silex": "src/" 277 | } 278 | }, 279 | "notification-url": "https://packagist.org/downloads/", 280 | "license": [ 281 | "MIT" 282 | ], 283 | "authors": [ 284 | { 285 | "name": "Fabien Potencier", 286 | "email": "fabien@symfony.com" 287 | }, 288 | { 289 | "name": "Igor Wiedler", 290 | "email": "igor@wiedler.ch" 291 | } 292 | ], 293 | "description": "The PHP micro-framework based on the Symfony2 Components", 294 | "homepage": "http://silex.sensiolabs.org", 295 | "keywords": [ 296 | "microframework" 297 | ], 298 | "time": "2014-09-26 09:32:30" 299 | }, 300 | { 301 | "name": "symfony/debug", 302 | "version": "v2.6.0", 303 | "target-dir": "Symfony/Component/Debug", 304 | "source": { 305 | "type": "git", 306 | "url": "https://github.com/symfony/Debug.git", 307 | "reference": "e1e27710efabc3f67a2d1f6710641b3bdca289d3" 308 | }, 309 | "dist": { 310 | "type": "zip", 311 | "url": "https://api.github.com/repos/symfony/Debug/zipball/e1e27710efabc3f67a2d1f6710641b3bdca289d3", 312 | "reference": "e1e27710efabc3f67a2d1f6710641b3bdca289d3", 313 | "shasum": "" 314 | }, 315 | "require": { 316 | "php": ">=5.3.3", 317 | "psr/log": "~1.0" 318 | }, 319 | "require-dev": { 320 | "symfony/http-foundation": "~2.1", 321 | "symfony/http-kernel": "~2.1" 322 | }, 323 | "suggest": { 324 | "symfony/http-foundation": "", 325 | "symfony/http-kernel": "" 326 | }, 327 | "type": "library", 328 | "extra": { 329 | "branch-alias": { 330 | "dev-master": "2.6-dev" 331 | } 332 | }, 333 | "autoload": { 334 | "psr-0": { 335 | "Symfony\\Component\\Debug\\": "" 336 | } 337 | }, 338 | "notification-url": "https://packagist.org/downloads/", 339 | "license": [ 340 | "MIT" 341 | ], 342 | "authors": [ 343 | { 344 | "name": "Symfony Community", 345 | "homepage": "http://symfony.com/contributors" 346 | }, 347 | { 348 | "name": "Fabien Potencier", 349 | "email": "fabien@symfony.com" 350 | } 351 | ], 352 | "description": "Symfony Debug Component", 353 | "homepage": "http://symfony.com", 354 | "time": "2014-11-28 10:00:40" 355 | }, 356 | { 357 | "name": "symfony/event-dispatcher", 358 | "version": "v2.5.7", 359 | "target-dir": "Symfony/Component/EventDispatcher", 360 | "source": { 361 | "type": "git", 362 | "url": "https://github.com/symfony/EventDispatcher.git", 363 | "reference": "bb6fc12085cd195dceaf48016087b12b632df497" 364 | }, 365 | "dist": { 366 | "type": "zip", 367 | "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/bb6fc12085cd195dceaf48016087b12b632df497", 368 | "reference": "bb6fc12085cd195dceaf48016087b12b632df497", 369 | "shasum": "" 370 | }, 371 | "require": { 372 | "php": ">=5.3.3" 373 | }, 374 | "require-dev": { 375 | "psr/log": "~1.0", 376 | "symfony/config": "~2.0", 377 | "symfony/dependency-injection": "~2.0,<2.6.0", 378 | "symfony/stopwatch": "~2.2" 379 | }, 380 | "suggest": { 381 | "symfony/dependency-injection": "", 382 | "symfony/http-kernel": "" 383 | }, 384 | "type": "library", 385 | "extra": { 386 | "branch-alias": { 387 | "dev-master": "2.5-dev" 388 | } 389 | }, 390 | "autoload": { 391 | "psr-0": { 392 | "Symfony\\Component\\EventDispatcher\\": "" 393 | } 394 | }, 395 | "notification-url": "https://packagist.org/downloads/", 396 | "license": [ 397 | "MIT" 398 | ], 399 | "authors": [ 400 | { 401 | "name": "Symfony Community", 402 | "homepage": "http://symfony.com/contributors" 403 | }, 404 | { 405 | "name": "Fabien Potencier", 406 | "email": "fabien@symfony.com" 407 | } 408 | ], 409 | "description": "Symfony EventDispatcher Component", 410 | "homepage": "http://symfony.com", 411 | "time": "2014-10-30 20:17:55" 412 | }, 413 | { 414 | "name": "symfony/http-foundation", 415 | "version": "v2.5.7", 416 | "target-dir": "Symfony/Component/HttpFoundation", 417 | "source": { 418 | "type": "git", 419 | "url": "https://github.com/symfony/HttpFoundation.git", 420 | "reference": "24545d3def96e6d6c3d7f1efdb48f330f27e244d" 421 | }, 422 | "dist": { 423 | "type": "zip", 424 | "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/24545d3def96e6d6c3d7f1efdb48f330f27e244d", 425 | "reference": "24545d3def96e6d6c3d7f1efdb48f330f27e244d", 426 | "shasum": "" 427 | }, 428 | "require": { 429 | "php": ">=5.3.3" 430 | }, 431 | "require-dev": { 432 | "symfony/expression-language": "~2.4" 433 | }, 434 | "type": "library", 435 | "extra": { 436 | "branch-alias": { 437 | "dev-master": "2.5-dev" 438 | } 439 | }, 440 | "autoload": { 441 | "psr-0": { 442 | "Symfony\\Component\\HttpFoundation\\": "" 443 | }, 444 | "classmap": [ 445 | "Symfony/Component/HttpFoundation/Resources/stubs" 446 | ] 447 | }, 448 | "notification-url": "https://packagist.org/downloads/", 449 | "license": [ 450 | "MIT" 451 | ], 452 | "authors": [ 453 | { 454 | "name": "Symfony Community", 455 | "homepage": "http://symfony.com/contributors" 456 | }, 457 | { 458 | "name": "Fabien Potencier", 459 | "email": "fabien@symfony.com" 460 | } 461 | ], 462 | "description": "Symfony HttpFoundation Component", 463 | "homepage": "http://symfony.com", 464 | "time": "2014-11-20 13:22:25" 465 | }, 466 | { 467 | "name": "symfony/http-kernel", 468 | "version": "v2.5.7", 469 | "target-dir": "Symfony/Component/HttpKernel", 470 | "source": { 471 | "type": "git", 472 | "url": "https://github.com/symfony/HttpKernel.git", 473 | "reference": "03b9f8fbfe75044ff6ca923e6471f117faf7b965" 474 | }, 475 | "dist": { 476 | "type": "zip", 477 | "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/03b9f8fbfe75044ff6ca923e6471f117faf7b965", 478 | "reference": "03b9f8fbfe75044ff6ca923e6471f117faf7b965", 479 | "shasum": "" 480 | }, 481 | "require": { 482 | "php": ">=5.3.3", 483 | "psr/log": "~1.0", 484 | "symfony/debug": "~2.5", 485 | "symfony/event-dispatcher": "~2.5", 486 | "symfony/http-foundation": "~2.5" 487 | }, 488 | "require-dev": { 489 | "symfony/browser-kit": "~2.2", 490 | "symfony/class-loader": "~2.1", 491 | "symfony/config": "~2.0", 492 | "symfony/console": "~2.2", 493 | "symfony/dependency-injection": "~2.0", 494 | "symfony/expression-language": "~2.4", 495 | "symfony/finder": "~2.0", 496 | "symfony/process": "~2.0", 497 | "symfony/routing": "~2.2", 498 | "symfony/stopwatch": "~2.2", 499 | "symfony/templating": "~2.2" 500 | }, 501 | "suggest": { 502 | "symfony/browser-kit": "", 503 | "symfony/class-loader": "", 504 | "symfony/config": "", 505 | "symfony/console": "", 506 | "symfony/dependency-injection": "", 507 | "symfony/finder": "" 508 | }, 509 | "type": "library", 510 | "extra": { 511 | "branch-alias": { 512 | "dev-master": "2.5-dev" 513 | } 514 | }, 515 | "autoload": { 516 | "psr-0": { 517 | "Symfony\\Component\\HttpKernel\\": "" 518 | } 519 | }, 520 | "notification-url": "https://packagist.org/downloads/", 521 | "license": [ 522 | "MIT" 523 | ], 524 | "authors": [ 525 | { 526 | "name": "Symfony Community", 527 | "homepage": "http://symfony.com/contributors" 528 | }, 529 | { 530 | "name": "Fabien Potencier", 531 | "email": "fabien@symfony.com" 532 | } 533 | ], 534 | "description": "Symfony HttpKernel Component", 535 | "homepage": "http://symfony.com", 536 | "time": "2014-11-20 16:00:03" 537 | }, 538 | { 539 | "name": "symfony/routing", 540 | "version": "v2.5.7", 541 | "target-dir": "Symfony/Component/Routing", 542 | "source": { 543 | "type": "git", 544 | "url": "https://github.com/symfony/Routing.git", 545 | "reference": "f3f5f31d5e227c51eed7ba8b1ed809a97ebaa341" 546 | }, 547 | "dist": { 548 | "type": "zip", 549 | "url": "https://api.github.com/repos/symfony/Routing/zipball/f3f5f31d5e227c51eed7ba8b1ed809a97ebaa341", 550 | "reference": "f3f5f31d5e227c51eed7ba8b1ed809a97ebaa341", 551 | "shasum": "" 552 | }, 553 | "require": { 554 | "php": ">=5.3.3" 555 | }, 556 | "require-dev": { 557 | "doctrine/annotations": "~1.0", 558 | "psr/log": "~1.0", 559 | "symfony/config": "~2.2", 560 | "symfony/expression-language": "~2.4", 561 | "symfony/http-foundation": "~2.3", 562 | "symfony/yaml": "~2.0" 563 | }, 564 | "suggest": { 565 | "doctrine/annotations": "For using the annotation loader", 566 | "symfony/config": "For using the all-in-one router or any loader", 567 | "symfony/expression-language": "For using expression matching", 568 | "symfony/yaml": "For using the YAML loader" 569 | }, 570 | "type": "library", 571 | "extra": { 572 | "branch-alias": { 573 | "dev-master": "2.5-dev" 574 | } 575 | }, 576 | "autoload": { 577 | "psr-0": { 578 | "Symfony\\Component\\Routing\\": "" 579 | } 580 | }, 581 | "notification-url": "https://packagist.org/downloads/", 582 | "license": [ 583 | "MIT" 584 | ], 585 | "authors": [ 586 | { 587 | "name": "Symfony Community", 588 | "homepage": "http://symfony.com/contributors" 589 | }, 590 | { 591 | "name": "Fabien Potencier", 592 | "email": "fabien@symfony.com" 593 | } 594 | ], 595 | "description": "Symfony Routing Component", 596 | "homepage": "http://symfony.com", 597 | "keywords": [ 598 | "router", 599 | "routing", 600 | "uri", 601 | "url" 602 | ], 603 | "time": "2014-11-03 20:24:10" 604 | } 605 | ], 606 | "packages-dev": [], 607 | "aliases": [], 608 | "minimum-stability": "stable", 609 | "stability-flags": [], 610 | "prefer-stable": false, 611 | "platform": { 612 | "php": ">=5.5.0" 613 | }, 614 | "platform-dev": [] 615 | } 616 | -------------------------------------------------------------------------------- /continuous-pipe.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | images: 3 | build: 4 | services: 5 | web: 6 | image: docker.io/sroze/php-example-manual 7 | naming_strategy: sha1 8 | 9 | deployment: 10 | deploy: 11 | cluster: ${CLUSTER} 12 | services: 13 | mysql: 14 | deployment_strategy: 15 | locked: true 16 | 17 | web: 18 | specification: 19 | accessibility: 20 | from_external: true 21 | 22 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | web: 4 | build: . 5 | depends_on: 6 | - mysql 7 | expose: 8 | - 80 9 | volumes: 10 | - .:/app 11 | mysql: 12 | image: mysql 13 | environment: 14 | MYSQL_ROOT_PASSWORD: root 15 | expose: 16 | - 3306 17 | -------------------------------------------------------------------------------- /docker/apache/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -xe 3 | 4 | # Start Apache with the right permissions 5 | /app/docker/apache/start_safe_perms -DFOREGROUND 6 | -------------------------------------------------------------------------------- /docker/apache/start_safe_perms: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## 4 | # Detect the ownership of the webroot 5 | # and run apache as that user. 6 | # 7 | main() { 8 | local owner group owner_id group_id tmp 9 | read owner group owner_id group_id < <(stat -c '%U %G %u %g' .) 10 | if [[ $owner = UNKNOWN ]]; then 11 | owner=$(randname) 12 | if [[ $group = UNKNOWN ]]; then 13 | group=$owner 14 | addgroup --system --gid "$group_id" "$group" 15 | fi 16 | adduser --system --uid=$owner_id --gid=$group_id "$owner" 17 | fi 18 | tmp=/tmp/$RANDOM 19 | { 20 | echo "User $owner" 21 | echo "Group $group" 22 | grep -v '^User' /etc/apache2/apache2.conf | 23 | grep -v '^Group' 24 | } >> "$tmp" && 25 | cat "$tmp" > /etc/apache2/apache2.conf && 26 | rm "$tmp" 27 | # Not volumes, so need to be chowned 28 | chown -R "$owner:$group" /var/{lock,log,run}/apache* 29 | exec /usr/sbin/apache2ctl "$@" 30 | } 31 | 32 | ## 33 | # Generate a random sixteen-character 34 | # string of alphabetical characters 35 | randname() { 36 | local -x LC_ALL=C 37 | tr -dc '[:lower:]' < /dev/urandom | 38 | dd count=1 bs=16 2>/dev/null 39 | } 40 | 41 | main "$@" 42 | -------------------------------------------------------------------------------- /docker/apache/vhost.conf: -------------------------------------------------------------------------------- 1 | 2 | ServerAdmin webmaster@localhost 3 | DocumentRoot /app/web 4 | 5 | 6 | Options Indexes FollowSymLinks 7 | AllowOverride All 8 | Require all granted 9 | 10 | 11 | -------------------------------------------------------------------------------- /docker/php/php.ini: -------------------------------------------------------------------------------- 1 | date.timezone = Europe/London 2 | -------------------------------------------------------------------------------- /src/App/Controller/StatusController.php: -------------------------------------------------------------------------------- 1 | get('/', function (Application $app, Request $request) { 25 | $output = ''; 26 | $status = 200; 27 | 28 | try { 29 | /** @var Connection $database */ 30 | $database = $app['db']; 31 | $database->executeQuery('CREATE TABLE IF NOT EXISTS hits (hitTime DATETIME NOT NULL DEFAULT NOW(), ip TEXT)'); 32 | 33 | $database->insert('hits', array( 34 | 'ip' => $request->getClientIp(), 35 | )); 36 | 37 | $result = $database->fetchAssoc('SELECT COUNT(*) as number FROM hits'); 38 | $output .= '

'.$result['number'].' hits !

'; 39 | $output .= '

Current server: '.$_SERVER['SERVER_ADDR'].'

'; 40 | 41 | $lastHits = $database->fetchAll('SELECT * FROM hits ORDER BY hits.hitTime DESC LIMIT 10'); 42 | $output .= '

Last 10 hits

'; 43 | $output .= ''; 44 | foreach ($lastHits as $hit) { 45 | $output .= ''; 46 | } 47 | 48 | $output .= '
DatetimeIP
'.$hit['hitTime'].''.$hit['ip'].'
'; 49 | $output .= '

Server

'; 50 | $output .= '
'.print_r($_SERVER, true).'
'; 51 | } catch (\Exception $e) { 52 | $output .= '

[EXCEPTION] '.$e->getMessage().'

'; 53 | $output .= '
'.$e->getTraceAsString().'
'; 54 | $output .= '
'; 55 | $output .= '
'.print_r($app['db.options'], true).'
'; 56 | $output .= '
'; 57 | $output .= '
'.print_r($_ENV, true).'
'; 58 | $output .= '
'; 59 | $output .= '
'.print_r($_SERVER, true).'
'; 60 | 61 | $status = 500; 62 | } 63 | 64 | return new Response($output, $status); 65 | }); 66 | 67 | return $controllers; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /web/index.php: -------------------------------------------------------------------------------- 1 | register(new DoctrineServiceProvider(), array( 11 | 'db.options' => array( 12 | 'driver' => 'pdo_mysql', 13 | 'host' => getenv('MYSQL_HOST') ?: 'mysql', 14 | 'port' => intval(getenv('DATABASE_PORT') ?: 3306), 15 | 'dbname' => getenv('DATABASE_NAME') ?: 'mysql', 16 | 'user' => getenv('DATABASE_USER') ?: 'root', 17 | 'password' => getenv('DATABASE_PASSWORD') ?: 'root', 18 | 'charset' => 'utf8', 19 | ), 20 | )); 21 | 22 | $app->get('/', function () { 23 | return new Response('

My PHP application running!

Go to status page to check the database.

'); 24 | }); 25 | 26 | $app->mount('/status', new StatusController()); 27 | $app->run(); 28 | --------------------------------------------------------------------------------