start rework for 2025
This commit is contained in:
parent
81c47d94ca
commit
b2fb8bee39
14 changed files with 214 additions and 1589 deletions
|
@ -2,29 +2,45 @@
|
|||
|
||||
### Front Controller
|
||||
|
||||
A [front controller](http://en.wikipedia.org/wiki/Front_Controller_pattern) is a single point of entry for your application.
|
||||
A [front controller](http://en.wikipedia.org/wiki/Front_Controller_pattern) is a single point of entry for your
|
||||
application.
|
||||
|
||||
To start, create an empty directory for your project. You also need an entry point where all requests will go to. This means you will have to create an `index.php` file.
|
||||
To start, create an empty directory for your project. You also need an entry point where all requests will go to. This
|
||||
means you will have to create an `index.php` file.
|
||||
|
||||
A common way to do this is to just put the `index.php` in the root folder of the projects. This is also how some frameworks do it. Let me explain why you should not do this.
|
||||
A common way to do this is to just put the `index.php` in the root folder of the projects. Let me explain why you should not do this.
|
||||
|
||||
The `index.php` is the starting point, so it has to be inside the web server directory. This means that the web server has access to all subdirectories. If you set things up properly, you can still prevent it from accessing your subfolders where your application files are.
|
||||
The `index.php` is the starting point, so it has to be inside the web server directory. This means that the web server
|
||||
has access to all subdirectories. If you set things up properly, you can still prevent it from accessing your subfolders
|
||||
where your application files are.
|
||||
|
||||
But sometimes things don't go according to plan. And if something goes wrong and your files are set up as above, your whole application source code could be exposed to visitors. I won't have to explain why this is not a good thing.
|
||||
But sometimes things don't go according to plan. And if something goes wrong and your files are set up as above, your
|
||||
whole application source code could be exposed to visitors. I won't have to explain why this is not a good thing.
|
||||
|
||||
So instead of doing that, create a folder in your project folder called `public`. This is a good time to create an `src` folder for your application, also in the project root folder.
|
||||
So instead of doing that, create a folder in your project folder called `public`. This is a good time to create an `src`
|
||||
folder for your application, also in the project root folder.
|
||||
|
||||
Inside the `public` folder you can now create your `index.php`. Remember that you don't want to expose anything here, so put just the following code in there:
|
||||
Inside the `public` folder you can now create your `index.php`. Remember that you don't want to expose anything here, so
|
||||
put just the following code in there:
|
||||
|
||||
```php
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/../src/Bootstrap.php';
|
||||
```
|
||||
|
||||
`__DIR__` is a [magic constant](http://php.net/manual/en/language.constants.predefined.php) that contains the path of the directory. By using it, you can make sure that the `require` always uses the same relative path to the file it is used in. Otherwise, if you call the `index.php` from a different folder it will not find the file.
|
||||
`__DIR__` is a [magic constant](http://php.net/manual/en/language.constants.predefined.php) that contains the path of
|
||||
the directory. By using it, you can make sure that the `require` always uses the same relative path to the file it is
|
||||
used in. Otherwise, if you call the `index.php` from a different folder it will not find the file.
|
||||
|
||||
`declare(strict_types = 1);` sets the current file to [strict typing](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration.strict). In this tutorial we are going to use this for all PHP files. This means that you can't just pass an integer as a parameter to a method that requires a string. If you don't use strict mode, it would be automatically casted to the required type. With strict mode, it will throw an Exception if it is the wrong type.
|
||||
`declare(strict_types=1);` sets the current file
|
||||
to [strict typing](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration.strict). In
|
||||
this tutorial we are going to use this for all PHP files. This means that you can't just pass an integer as a parameter
|
||||
to a method that requires a string. If you don't use strict mode, it would be automatically cast to the required type.
|
||||
With strict mode, it will throw an Exception if it is the wrong type. I would always advise you to use strict mode as
|
||||
that helps you catch possible errors early.
|
||||
|
||||
The `Bootstrap.php` will be the file that wires your application together. We will get to it shortly.
|
||||
|
||||
|
@ -33,18 +49,26 @@ The rest of the public folder is reserved for your public asset files (like Java
|
|||
Now navigate inside your `src` folder and create a new `Bootstrap.php` file with the following content:
|
||||
|
||||
```php
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
echo 'Hello World!';
|
||||
```
|
||||
|
||||
Now let's see if everything is set up correctly. Open up a console and navigate into your projects `public` folder. In there type `php -S 0.0.0.0:1235` and press enter. This will start the built-in webserver and you can access your page in a browser with `http://localhost:1235`. You should now see the 'hello world' message.
|
||||
Now let's see if everything is set up correctly. Open up a console and navigate into your projects `public` folder. In
|
||||
there type `php -S 0.0.0.0:1235` and press enter. This will start the built-in webserver and you can access your page in
|
||||
a browser with `http://localhost:1235`. You should now see the 'hello world' message.
|
||||
|
||||
If there is an error, go back and try to fix it. If you only see a blank page, check the console window where the server is running for errors.
|
||||
If there is an error, go back and try to fix it. If you only see a blank page, check the console window where the server
|
||||
is running for errors.
|
||||
|
||||
Now would be a good time to commit your progress. If you are not already using Git, set up a repository now. This is not a Git tutorial so I won't go over the details. But using version control should be a habit, even if it is just for a tutorial project like this.
|
||||
Now would be a good time to commit your progress. If you are not already using Git, set up a repository now. This is not
|
||||
a Git tutorial so I won't go over the details. But using version control should be a habit, even if it is just for a
|
||||
tutorial project like this.
|
||||
|
||||
Some editors and IDE's put their own files into your project folders. If that is the case, create a `.gitignore` file in your project root and exclude the files/directories. Below is an example for PHPStorm:
|
||||
Some editors and IDE's put their own files into your project folders. If that is the case, create a `.gitignore` file in
|
||||
your project root and exclude the files/directories. Below is an example for PHPStorm:
|
||||
|
||||
```
|
||||
.idea/
|
||||
|
|
|
@ -17,21 +17,23 @@ Add the following content to the file:
|
|||
|
||||
```json
|
||||
{
|
||||
"name": "lubian/no-framework",
|
||||
"require": {
|
||||
"php": "^8.1"
|
||||
},
|
||||
"name": "lubiana/no-framework",
|
||||
"type": "project",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Lubian\\NoFramework\\": "src/"
|
||||
"Lubiana\\NoFramework\\": "src/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "example",
|
||||
"email": "test@example.com"
|
||||
"name": "lubiana",
|
||||
"email": "lubiana@hannover.ccc.de"
|
||||
}
|
||||
]
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.4"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -39,6 +41,9 @@ In the autoload part you can see that I am using the `Lubian\NoFramework` namesp
|
|||
whatever fits your project there, but from now on I will always use the `Lubian\NoFramework` namespace in my examples.
|
||||
Just replace it with your namespace in your own code.
|
||||
|
||||
In the `require` block I specified, that the minimum PHP Version to run this code is 8.4. That helps composer to find
|
||||
suitable versions of libraries we want to install later on.
|
||||
|
||||
I have also defined, that all my code and classes in the 'Lubian\NoFramework' namespace lives under the './src' folder.
|
||||
|
||||
As the Bootstrap.php file is placed in that directory we should
|
||||
|
@ -46,9 +51,11 @@ add the namespace to the File as well. Here is my current Bootstrap.php
|
|||
as a reference:
|
||||
|
||||
```php
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
namespace Lubian\NoFramework;
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lubiana\NoFramework;
|
||||
|
||||
echo 'Hello World!';
|
||||
```
|
||||
|
|
|
@ -4,29 +4,68 @@
|
|||
|
||||
An error handler allows you to customize what happens if your code results in an error.
|
||||
|
||||
A nice error page with a lot of information for debugging goes a long way during development. So the first package
|
||||
It is important to clearly define how your project should behave when it encounters an error, and especially where it
|
||||
should output the information about an error.
|
||||
|
||||
We want to mainly focus on Exceptions, which are an encapsulation of errors in php. We also have "normal" errors, but
|
||||
most of them are currently being changed currently into Exceptions.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lubiana\NoFramework;
|
||||
|
||||
use ErrorException;
|
||||
use Throwable;
|
||||
|
||||
set_exception_handler(function (Throwable $t) {
|
||||
$errorType = match($t->getCode()) {
|
||||
E_ERROR, E_USER_ERROR => 'Fatal Error',
|
||||
E_WARNING, E_USER_WARNING => 'Warning',
|
||||
E_NOTICE, E_USER_NOTICE => 'Notice',
|
||||
default => 'Unknown Error'
|
||||
};
|
||||
|
||||
echo <<<HTML
|
||||
<h1>{$errorType}</h1>
|
||||
<p>{$t->getMessage()}</p>
|
||||
<pre>{$t->getTraceAsString()}</pre>
|
||||
HTML;
|
||||
});
|
||||
|
||||
set_error_handler(
|
||||
function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
throw new ErrorException(
|
||||
message: $errstr,
|
||||
code: $errno,
|
||||
severity: $errno,
|
||||
filename: $errfile,
|
||||
line: $errline
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
echo 'Hello world!';
|
||||
```
|
||||
|
||||
You can then replace `echo 'Hello world!';` with `trigger_error('This is a test error');`
|
||||
or `throw new Exception('This is a test exception');` and open it in your browser to see if the error handling works.
|
||||
|
||||
|
||||
During development there are some other nice features to add. For example a quick link to open your Editor on the file the Error occured. So the first package
|
||||
for your application will take care of that.
|
||||
|
||||
I like [filp/whoops](https://github.com/filp/whoops), so I will show how you can install that package for your project.
|
||||
If you prefer another package, feel free to install that one. This is the beauty of programming without a framework,
|
||||
you have total control over your project.
|
||||
|
||||
An alternative package would be: [PHP-Error](https://github.com/JosephLenton/PHP-Error)
|
||||
Some alternatives would be: [PHP-Error](https://github.com/JosephLenton/PHP-Error) or [Tracy](https://tracy.nette.org/en/)
|
||||
|
||||
To install a new package, open up your `composer.json` and add the package to the require part. It should now look
|
||||
like this:
|
||||
|
||||
```php
|
||||
"require": {
|
||||
"php": ">=8.1.0",
|
||||
"filp/whoops": "^2.14"
|
||||
},
|
||||
```
|
||||
|
||||
Now run `composer update` in your console, and it will be installed.
|
||||
|
||||
Another way to install packages is to simply type "composer require filp/whoops" into your terminal at the project root,
|
||||
i that case composer automatically installs the package and updates your composer.json-file.
|
||||
To install that package into your project simply type `composer require filp/whoops` into your terminal at the project root,
|
||||
now composer automatically looks for a version of that package compatible with the rest of your project and your php
|
||||
version.
|
||||
|
||||
But you can't use it yet. PHP won't know where to find the files for the classes. For this you will need an autoloader,
|
||||
ideally a [PSR-4](http://www.php-fig.org/psr/psr-4/) autoloader. Composer already takes care of this for you, so you
|
||||
|
@ -36,7 +75,7 @@ only have to add a `require __DIR__ . '/../vendor/autoload.php';` to your `Boots
|
|||
can help someone to gain access to your system. Always show a user-friendly error page instead and send an email to
|
||||
yourself, write to a log or something similar. So only you can see the errors in the production environment.
|
||||
|
||||
For development that does not make sense though -- you want a nice error page. The solution is to have an environment
|
||||
For development that does not make sense, though -- you want a nice error page. The solution is to have an environment
|
||||
switch in your code. We use the getenv() function here to check the environment and define the 'dev' env as standard in
|
||||
case no environment has been set.
|
||||
|
||||
|
@ -44,36 +83,51 @@ Then after the error handler registration, throw an `Exception` to test if every
|
|||
Your `Bootstrap.php` should now look similar to this:
|
||||
|
||||
```php
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
namespace Lubian\NoFramework;
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lubiana\NoFramework;
|
||||
|
||||
use Throwable;
|
||||
use Whoops\Handler\CallbackHandler;
|
||||
use Whoops\Handler\PrettyPageHandler;
|
||||
use Whoops\Run;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$environment = getenv('ENVIRONMENT') ?: 'dev';
|
||||
|
||||
error_reporting(E_ALL);
|
||||
$environment = getenv('APP_ENV') ?: 'dev';
|
||||
|
||||
$whoops = new Run;
|
||||
$whoops->pushHandler(
|
||||
new CallbackHandler(
|
||||
function (Throwable $e) use ($environment) {
|
||||
if ($environment !== 'dev') {
|
||||
http_response_code(500);
|
||||
echo 'Whoops';
|
||||
}
|
||||
error_log(<<<TXT
|
||||
Error: {$e->getMessage()}
|
||||
{$e->getTraceAsString()}
|
||||
TXT
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if ($environment === 'dev') {
|
||||
$whoops->pushHandler(new PrettyPageHandler);
|
||||
} else {
|
||||
$whoops->pushHandler(function (\Throwable $e) {
|
||||
error_log("Error: " . $e->getMessage(), $e->getCode());
|
||||
echo 'An Error happened';
|
||||
});
|
||||
}
|
||||
$whoops->register();
|
||||
|
||||
throw new \Exception("Ooooopsie");
|
||||
throw new \Exception('Hello world');
|
||||
|
||||
```
|
||||
|
||||
You should now see a error page with the line highlighted where you throw the exception. If not, go back and debug until
|
||||
you get it working. Now would also be a good time for another commit.
|
||||
|
||||
**Side-note:** Here we use `getenv()` to read a Variable from the Environment and fallback to using 'dev' if it is not set. That is a bad default, and in a production app you should always default to the strictest mode.
|
||||
There are also good libraries that help with managing that in a better fashion. You can take a look at [phpdotenv](https://github.com/vlucas/phpdotenv) or [symfony/dotenv](https://github.com/symfony/dotenv) and maybe implement them. This tutorial however skips this step.
|
||||
|
||||
[<< previous](02-composer.md) | [next >>](04-development-helpers.md)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
5
implementation/01/public/index.php
Normal file
5
implementation/01/public/index.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/../Bootstrap.php';
|
5
implementation/01/src/Bootstrap.php
Normal file
5
implementation/01/src/Bootstrap.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
echo 'Hello world!';
|
19
implementation/02/composer.json
Normal file
19
implementation/02/composer.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "lubiana/no-framework",
|
||||
"type": "project",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Lubiana\\NoFramework\\": "src/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "lubiana",
|
||||
"email": "lubiana@hannover.ccc.de"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.4"
|
||||
}
|
||||
}
|
5
implementation/02/public/index.php
Normal file
5
implementation/02/public/index.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/../Bootstrap.php';
|
7
implementation/02/src/Bootstrap.php
Normal file
7
implementation/02/src/Bootstrap.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lubiana\NoFramework;
|
||||
|
||||
echo 'Hello world!';
|
|
@ -1,5 +1,7 @@
|
|||
{
|
||||
"name": "lubiana/no-framework",
|
||||
"type": "project",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Lubiana\\NoFramework\\": "src/"
|
||||
|
@ -7,12 +9,12 @@
|
|||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "example",
|
||||
"email": "test@example.com"
|
||||
"name": "lubiana",
|
||||
"email": "lubiana@hannover.ccc.de"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"filp/whoops": "^2.14"
|
||||
"php": "^8.4",
|
||||
"filp/whoops": "^2.18"
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
|
@ -1,3 +1,5 @@
|
|||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/../src/Bootstrap.php';
|
|
@ -1,27 +1,38 @@
|
|||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
namespace Lubian\NoFramework;
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lubiana\NoFramework;
|
||||
|
||||
use Throwable;
|
||||
use Whoops\Handler\CallbackHandler;
|
||||
use Whoops\Handler\PrettyPageHandler;
|
||||
use Whoops\Run;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$environment = getenv('ENVIRONMENT') ?: 'dev';
|
||||
$environment = getenv('APP_ENV') ?: 'dev';
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
$whoops = new Run();
|
||||
$whoops = new Run;
|
||||
$whoops->pushHandler(
|
||||
new CallbackHandler(
|
||||
function (Throwable $e) use ($environment) {
|
||||
if ($environment !== 'dev') {
|
||||
http_response_code(500);
|
||||
echo 'Whoops';
|
||||
}
|
||||
error_log(<<<TXT
|
||||
Error: {$e->getMessage()}
|
||||
{$e->getTraceAsString()}
|
||||
TXT
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if ($environment === 'dev') {
|
||||
$whoops->pushHandler(new PrettyPageHandler());
|
||||
} else {
|
||||
$whoops->pushHandler(function (\Throwable $t) {
|
||||
error_log('ERROR: ' . $t->getMessage(), $t->getCode());
|
||||
echo 'Oooopsie';
|
||||
});
|
||||
$whoops->pushHandler(new PrettyPageHandler);
|
||||
}
|
||||
|
||||
$whoops->register();
|
||||
|
||||
echo 'Hello World!';
|
||||
throw new \Exception('Hello world');
|
1516
implementation/12/composer.lock
generated
1516
implementation/12/composer.lock
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue