[Initial Commit]
This commit is contained in:
commit
6b55ae7c70
11 changed files with 360 additions and 0 deletions
6
.gitignore
vendored
Executable file
6
.gitignore
vendored
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
vendor
|
||||||
|
public
|
||||||
|
composer.lock
|
78
README.md
Executable file
78
README.md
Executable file
|
@ -0,0 +1,78 @@
|
||||||
|
|
||||||
|
# Docker PHP Development Environment
|
||||||
|
|
||||||
|
This repository allows you to quickly get a development environment up and running using Docker, including a MySQL database and some debugging tools.
|
||||||
|
|
||||||
|
This environment includes the latest versions of the following software:
|
||||||
|
|
||||||
|
- `nginx:alpine`
|
||||||
|
- `php:8.3-fpm`
|
||||||
|
- `mysql:latest`
|
||||||
|
- `xdebug`
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker - https://www.docker.com/products/docker-desktop
|
||||||
|
- Tested and confirmed working on:
|
||||||
|
- macOS 12.3 (Monterey)
|
||||||
|
- Windows 10 Version 21H1 build 19043.1165
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
Run `composer create-project jlucki/docker-php-dev-env "project_name"`
|
||||||
|
|
||||||
|
Alternatively, download the files from this repo and copy them to your project directory.
|
||||||
|
|
||||||
|
## Running the Container
|
||||||
|
|
||||||
|
1. Run `docker compose up` from the project directory
|
||||||
|
- Note: the above command will only work on versions newer than 3.6. If you are using Docker 3.6 or older, use `docker-compose up`
|
||||||
|
2. Visit `localhost` in your browser
|
||||||
|
|
||||||
|
## Setting up Xdebug in Your IDE
|
||||||
|
|
||||||
|
_The following instructions are for PHPStorm._
|
||||||
|
|
||||||
|
1. Click on Run > Edit Configurations
|
||||||
|
2. Click the `+` and add a new PHP Remote Debug configuration
|
||||||
|
3. Give the configuration a name
|
||||||
|
4. Tick _Filter debug connection by IDE key_
|
||||||
|
5. Add `PHPSTORM` in the session id
|
||||||
|
6. Click the `...` next to Server
|
||||||
|
7. Click the `+` to add a new server
|
||||||
|
8. Give the server a name, and set the host to `localhost`
|
||||||
|
9. Tick _Use path mappings_
|
||||||
|
10. Under the `Project files` file/directory, next to your project folder, for the `Absolute path on the server` column add `/var/www/html`
|
||||||
|
11. Hit Apply and OK until you've returned to the IDE
|
||||||
|
12. Click the _Start Listening for PHP Debug Connections_ icon
|
||||||
|
13. Set a breakpoint in your code
|
||||||
|
14. Refresh the page in your browser
|
||||||
|
15. Accept _Incoming Connection From Xdebug_
|
||||||
|
16. Happy debugging!
|
||||||
|
|
||||||
|
See https://xdebug.org/docs/remote for how to enable xdebug sessions.
|
||||||
|
|
||||||
|
## Additional Notes
|
||||||
|
|
||||||
|
For database connections, use `mysql` as the hostname. If you would like a different hostname, change the service name in the `docker-compose.yml` file.
|
||||||
|
|
||||||
|
## Windows 10 Troubleshooting
|
||||||
|
|
||||||
|
If you are having problems and can't seem to fix the issue, try one or both of these fixes:
|
||||||
|
|
||||||
|
### Solution 1
|
||||||
|
Try switching to **Windows containers**:
|
||||||
|
|
||||||
|
Right-click the Docker icon in the system tray and click **Switch to Windows containers** as seen below:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Solution 2
|
||||||
|
If the **Use the WSL 2 based engine** setting is checked:
|
||||||
|
|
||||||
|
Uncheck the setting, then enable file sharing located under the **Resources** section, then the **File Sharing** section as seen below:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If the **Use the WSL 2 based engine** setting is unchecked:
|
||||||
|
Enable/check the setting (file sharing is automatically configured)
|
31
composer.json
Executable file
31
composer.json
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"name": "jlucki/docker-php-dev-env",
|
||||||
|
"description": "A simple web development environment using Docker with NGINX, PHP, MySQL and Xdebug.",
|
||||||
|
"type": "project",
|
||||||
|
"keywords": [
|
||||||
|
"docker",
|
||||||
|
"development",
|
||||||
|
"environment",
|
||||||
|
"php",
|
||||||
|
"xdebug",
|
||||||
|
"nginx"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jan Lucki",
|
||||||
|
"email": "jan@lucki.dev"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"sabre/dav": "^4.6",
|
||||||
|
"ext-curl": "*"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"Keycloak": "src",
|
||||||
|
"Collections": "src",
|
||||||
|
"Principal": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
docker-compose.yml
Executable file
24
docker-compose.yml
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
version: '3.7'
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx:alpine
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- ./:/var/www/html
|
||||||
|
- ./docker/conf/default.conf:/etc/nginx/conf.d/default.conf
|
||||||
|
php:
|
||||||
|
build:
|
||||||
|
context: ./docker
|
||||||
|
volumes:
|
||||||
|
- ./:/var/www/html
|
||||||
|
- ./docker/php/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini
|
||||||
|
mysql:
|
||||||
|
image: mysql:latest
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: root
|
||||||
|
MYSQL_DATABASE: webapp
|
||||||
|
MYSQL_USER: webapp
|
||||||
|
MYSQL_PASSWORD: root
|
15
docker/Dockerfile
Executable file
15
docker/Dockerfile
Executable file
|
@ -0,0 +1,15 @@
|
||||||
|
FROM php:8.3-fpm
|
||||||
|
|
||||||
|
ARG WITH_XDEBUG=true
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libjpeg62-turbo-dev \
|
||||||
|
libpng-dev \
|
||||||
|
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||||
|
&& docker-php-ext-install -j$(nproc) gd \
|
||||||
|
&& docker-php-ext-install mysqli \
|
||||||
|
&& docker-php-ext-install pdo pdo_mysql; \
|
||||||
|
if [ $WITH_XDEBUG = "true" ] ; then \
|
||||||
|
pecl install xdebug; \
|
||||||
|
fi ;
|
22
docker/conf/default.conf
Executable file
22
docker/conf/default.conf
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
index index.php index.html;
|
||||||
|
server_name localhost;
|
||||||
|
error_log /var/log/nginx/error.log;
|
||||||
|
access_log /var/log/nginx/access.log;
|
||||||
|
root /var/www/html/src;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri.js /index.php$is_args$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass php:9000;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
}
|
6
docker/php/xdebug.ini
Executable file
6
docker/php/xdebug.ini
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
[Xdebug]
|
||||||
|
zend_extension=xdebug.so
|
||||||
|
xdebug.mode=debug
|
||||||
|
xdebug.client_port = 9000
|
||||||
|
xdebug.client_host = host.docker.internal
|
||||||
|
xdebug.idekey=PHPSTORM
|
40
index.php
Executable file
40
index.php
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Collections\HomeCollection;
|
||||||
|
use Sabre\DAV;
|
||||||
|
|
||||||
|
// The autoloader
|
||||||
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
|
$aclPlugin = new \Sabre\DAVACL\Plugin();
|
||||||
|
|
||||||
|
|
||||||
|
// Set Auth
|
||||||
|
$authBackend = new Keycloak\KeycloakAuth($aclPlugin,$_ENV['client_id'], $_ENV['client_secret'], $_ENV['keycloak_token_url'] );
|
||||||
|
$authBackend->setRealm($_ENV['realm']);
|
||||||
|
$authPlugin = new DAV\Auth\Plugin($authBackend);
|
||||||
|
|
||||||
|
|
||||||
|
// The server object is responsible for making sense out of the WebDAV protocol
|
||||||
|
$server = new DAV\Server([new HomeCollection($authPlugin, $_ENV['users_path'])]);
|
||||||
|
|
||||||
|
// If your server is not on your webroot, make sure the following line has the
|
||||||
|
// correct information
|
||||||
|
$server->setBaseUri($_ENV['base_uri']);
|
||||||
|
|
||||||
|
// The lock manager is responsible for making sure users don't overwrite
|
||||||
|
// each others changes.
|
||||||
|
$lockBackend = new DAV\Locks\Backend\File('data/locks');
|
||||||
|
$lockPlugin = new DAV\Locks\Plugin($lockBackend);
|
||||||
|
$server->addPlugin($lockPlugin);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This ensures that we get a pretty index in the browser, but it is
|
||||||
|
// optional.
|
||||||
|
$server->addPlugin(new DAV\Browser\Plugin());
|
||||||
|
$server->addPlugin($authPlugin);
|
||||||
|
$server->addPlugin($aclPlugin);
|
||||||
|
|
||||||
|
// All we need to do now, is to fire up the server
|
||||||
|
$server->start();
|
38
src/Collections/HomeCollection.php
Normal file
38
src/Collections/HomeCollection.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Collections;
|
||||||
|
|
||||||
|
use Sabre\DAV\Collection;
|
||||||
|
use Sabre\DAV\Auth\Plugin as AuthPlugin;
|
||||||
|
use Sabre\DAV\FS\Directory;
|
||||||
|
|
||||||
|
class HomeCollection extends Collection
|
||||||
|
{
|
||||||
|
private $plugin;
|
||||||
|
private $userPath;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(AuthPlugin $authPlugin, $userPath)
|
||||||
|
{
|
||||||
|
$this->plugin = $authPlugin;
|
||||||
|
$this->userPath = $userPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getChildren()
|
||||||
|
{
|
||||||
|
$principal = $this->plugin->getCurrentPrincipal();
|
||||||
|
$username = explode("/", $principal)[1];
|
||||||
|
$path = $this->userPath;
|
||||||
|
|
||||||
|
if (!is_dir($path.$username)) {
|
||||||
|
mkdir($path.$username, 0777 , true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [new Directory($path.$username, $username)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return "Home";
|
||||||
|
}
|
||||||
|
}
|
56
src/Keycloak/KeycloakAuth.php
Normal file
56
src/Keycloak/KeycloakAuth.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Keycloak;
|
||||||
|
|
||||||
|
use Sabre\DAV\Auth\Backend\AbstractBasic;
|
||||||
|
use Sabre\DAVACL\Plugin as AclPlugin;
|
||||||
|
|
||||||
|
class KeycloakAuth extends AbstractBasic
|
||||||
|
{
|
||||||
|
private $aclPlugin;
|
||||||
|
private $client_id;
|
||||||
|
private $client_secret;
|
||||||
|
private $keycloakTokenUrl;
|
||||||
|
|
||||||
|
public function __construct(AclPlugin $plugin, $client_id, $client_secret, $keycloakTokenUrl)
|
||||||
|
{
|
||||||
|
$this->aclPlugin = $plugin;
|
||||||
|
$this->client_id = $client_id;
|
||||||
|
$this->client_secret = $client_secret;
|
||||||
|
$this->keycloakTokenUrl = $keycloakTokenUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validateUserPass($username, $password)
|
||||||
|
{
|
||||||
|
$curl = curl_init();
|
||||||
|
|
||||||
|
curl_setopt_array($curl, [
|
||||||
|
CURLOPT_URL => $this->keycloakTokenUrl,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_ENCODING => "",
|
||||||
|
CURLOPT_MAXREDIRS => 10,
|
||||||
|
CURLOPT_TIMEOUT => 30,
|
||||||
|
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||||
|
CURLOPT_CUSTOMREQUEST => "POST",
|
||||||
|
CURLOPT_POSTFIELDS => "grant_type=password&client_id=" . $this->client_id . "&client_secret=" . $this->client_secret . "&username=".$username."&password=".$password,
|
||||||
|
CURLOPT_HTTPHEADER => [
|
||||||
|
"Content-Type: application/x-www-form-urlencoded"
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
curl_exec($curl);
|
||||||
|
|
||||||
|
$err = curl_error($curl);
|
||||||
|
|
||||||
|
$data = curl_getinfo($curl);
|
||||||
|
|
||||||
|
curl_close($curl);
|
||||||
|
|
||||||
|
if ($err || $data['http_code'] != 200) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
44
src/Principal/CustomBackend.php
Normal file
44
src/Principal/CustomBackend.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Principal;
|
||||||
|
|
||||||
|
use Sabre\DAVACL\PrincipalBackend\AbstractBackend;
|
||||||
|
|
||||||
|
class CustomBackend extends AbstractBackend
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getPrincipalsByPrefix($prefixPath)
|
||||||
|
{
|
||||||
|
// TODO: Implement getPrincipalsByPrefix() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrincipalByPath($path)
|
||||||
|
{
|
||||||
|
// TODO: Implement getPrincipalByPath() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch)
|
||||||
|
{
|
||||||
|
// TODO: Implement updatePrincipal() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof')
|
||||||
|
{
|
||||||
|
// TODO: Implement searchPrincipals() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroupMemberSet($principal)
|
||||||
|
{
|
||||||
|
// TODO: Implement getGroupMemberSet() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroupMembership($principal)
|
||||||
|
{
|
||||||
|
// TODO: Implement getGroupMembership() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGroupMemberSet($principal, array $members)
|
||||||
|
{
|
||||||
|
// TODO: Implement setGroupMemberSet() method.
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue