├── .editorconfig
├── .gitignore
├── .htaccess
├── LICENSE
├── README.md
├── app
├── HomeController.php
├── IntroController.php
├── PhotoController.php
└── index.php
├── backup.sh
├── backups
└── .gitignore
├── composer.json
├── composer.lock
├── config
├── .env.example
└── .gitignore
├── deploy.sh
├── i18n.sh
├── index.php
├── locale
└── .gitignore
├── maintenance.php
├── public
├── css
│ └── index.html
├── favicon.ico
├── img
│ └── index.html
├── js
│ └── index.html
├── media
│ └── index.html
└── robots.txt
├── storage
├── app
│ └── .gitignore
└── framework
│ └── views
│ └── cache
│ └── .gitignore
└── views
├── 404.html.twig
├── greeting.html.twig
├── includes
├── flash
│ └── bootstrap-v3.html.twig
├── footer.html.twig
└── header.html.twig
├── mail
├── de-DE
│ ├── confirm-email.txt.twig
│ ├── email-changed.txt.twig
│ ├── forgot-password.txt.twig
│ ├── includes
│ │ ├── footer.txt.twig
│ │ └── header.txt.twig
│ ├── password-changed.txt.twig
│ └── sign-up.txt.twig
└── en-US
│ ├── confirm-email.txt.twig
│ ├── email-changed.txt.twig
│ ├── forgot-password.txt.twig
│ ├── includes
│ ├── footer.txt.twig
│ └── header.txt.twig
│ ├── password-changed.txt.twig
│ └── sign-up.txt.twig
└── welcome.html.twig
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = tab
7 | trim_trailing_whitespace = true
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | indent_style = space
13 | indent_size = 4
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # IntelliJ
2 | .idea/
3 |
4 | # Composer
5 | vendor/
6 | composer.phar
7 |
--------------------------------------------------------------------------------
/.htaccess:
--------------------------------------------------------------------------------
1 | ### PHP-Foundation (https://github.com/delight-im/PHP-Foundation)
2 | ### Copyright (c) delight.im (https://www.delight.im/)
3 | ### Licensed under the MIT License (https://opensource.org/licenses/MIT)
4 |
5 | ########## BEGIN MAINTENANCE MODE ##########
6 |
7 |
8 |
9 | RewriteEngine On
10 |
11 | # Enable maintenance mode (Uncomment 1 line below)
12 | # RewriteRule . maintenance.php [END]
13 |
14 |
15 |
16 | ########## END MAINTENANCE MODE ##########
17 |
18 | ########## BEGIN PERFORMANCE AND SECURITY (https://github.com/delight-im/htaccess) ##########
19 |
20 |
21 |
22 | # Prevent clickjacking (forbids framing by third-party sites)
23 | Header set X-Frame-Options sameorigin
24 |
25 | # Prevent content sniffing (MIME sniffing)
26 | Header set X-Content-Type-Options nosniff
27 |
28 | # Attempt to enable XSS filters in browsers, if available, and block reflected XSS
29 | Header set X-XSS-Protection "1; mode=block"
30 |
31 | # Cache media files for a month
32 |
33 | Header set Cache-Control max-age=2629800
34 |
35 |
36 | # Remove response headers that provide no value but leak information
37 | Header unset X-Powered-By
38 |
39 | # Disable "ETag" headers so that browsers rely on the "Cache-Control" and "Expires" headers
40 | Header unset ETag
41 |
42 |
43 |
44 |
45 |
46 | # Turn off directory listings for folders without default documents
47 | Options -Indexes
48 |
49 |
50 |
51 |
52 |
53 | # Disable 'MultiViews' implicit filename pattern matches
54 | Options -MultiViews
55 |
56 |
57 |
58 | # Serve "text/plain" and "text/html" documents as UTF-8 by default
59 | AddDefaultCharset utf-8
60 |
61 | # Disable "ETag" headers so that browsers rely on the "Cache-Control" and "Expires" headers
62 | FileETag None
63 |
64 | ########## END PERFORMANCE AND SECURITY ##########
65 |
66 | ########## BEGIN CUSTOM (YOUR RULES GO HERE) ##########
67 |
68 |
69 |
70 | # Enable HTTP Strict Transport Security (HSTS) with a duration of six months (Uncomment 1 line below)
71 | # Header set Strict-Transport-Security max-age=15778800
72 |
73 |
74 |
75 |
76 |
77 | RewriteEngine On
78 |
79 | # Force 'www' (i.e. prefix the "bare" domain and all subdomains with 'www' through permanent redirects) (Uncomment 4 lines below)
80 | # RewriteCond %{HTTP_HOST} !^$
81 | # RewriteCond %{HTTP_HOST} !^www\. [NC]
82 | # RewriteCond %{HTTPS}s ^on(s)|
83 | # RewriteRule ^ http%1://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
84 |
85 | # Force HTTPS (Uncomment 2 lines below)
86 | # RewriteCond %{HTTPS} off
87 | # RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
88 |
89 |
90 |
91 | # Prevent access to non-minified CSS and JS (Uncomment 3 lines below)
92 | #
93 | # Require all denied
94 | #
95 |
96 | # Announce contact information for security issues (Uncomment 2 lines below)
97 | # Header set X-Vulnerability-Disclosure "https://www.example.com/security"
98 | # Header set X-Security-Contact "security@example.com"
99 |
100 | ########## END CUSTOM ##########
101 |
102 | ########## BEGIN ROUTING (https://github.com/delight-im/PHP-Router) ##########
103 |
104 |
105 |
106 | RewriteEngine On
107 |
108 | # Don't rewrite requests for files in the 'public' directory
109 | RewriteRule ^(public)($|/) - [L]
110 |
111 | # For all other files first check if they exist in the 'public' directory
112 | RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f
113 | RewriteRule ^ public%{REQUEST_URI} [L]
114 |
115 | # And let 'index.php' handle everything else
116 | RewriteRule . index.php [L]
117 |
118 |
119 |
120 | ########## END ROUTING ##########
121 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) delight.im (https://www.delight.im/)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP-Foundation
2 |
3 | **Writing modern PHP applications efficiently**
4 |
5 | Build *better* applications *faster*, while still using “plain old PHP” only.
6 |
7 | There are no DSLs or pseudo-languages that you need to learn (except [Twig](views/welcome.html)!), nor any “magical” command-line utilities.
8 |
9 | ## Requirements
10 |
11 | * Apache HTTP Server 2.2.0+
12 | * `mod_rewrite`
13 | * `mod_headers`
14 | * PHP 5.6.0+
15 | * Multibyte String extension (`mbstring`)
16 | * PDO (PHP Data Objects) extension (`pdo`)
17 | * OpenSSL extension (`openssl`)
18 | * GMP (GNU Multiple Precision) extension (`gmp`)
19 | * MySQL 5.5.3+ **or** MariaDB 5.5.23+ **or** PostgreSQL 9.5.10+ **or** SQLite 3.14.1+
20 |
21 | ## Installation
22 |
23 | 1. Copy all files from this repository to your development path or web server.
24 |
25 | **Note:** This framework does currently work in the web root (at `/` under your domain) only.
26 |
27 | 1. Copy the configuration template `config/.env.example` to `config/.env`. This new file is where your private configuration will be stored. Fill in the correct settings for your environment. The most important setting is `APP_PUBLIC_URL`, which is required for your application to work correctly in the first place.
28 | 1. If you want to use the built-in authentication component, create the database tables for [MariaDB](https://github.com/delight-im/PHP-Auth/blob/master/Database/MySQL.sql), [MySQL](https://github.com/delight-im/PHP-Auth/blob/master/Database/MySQL.sql), [PostgreSQL](https://github.com/delight-im/PHP-Auth/blob/master/Database/PostgreSQL.sql) or [SQLite](https://github.com/delight-im/PHP-Auth/blob/master/Database/SQLite.sql) in the database that you specified in `config/.env`.
29 | 1. Get Composer [[?]](https://github.com/delight-im/Knowledge/blob/master/Composer%20(PHP).md) and run
30 |
31 | ```
32 | # composer check-platform-reqs
33 | $ composer install
34 | ```
35 |
36 | in this directory (containing the `README.md`) to set up the framework and its dependencies.
37 | 1. Make sure that the content of the `storage/` directory is writable by the web server, e.g. using
38 |
39 | ```
40 | $ sudo chown -R www-data:www-data /var/www/html/storage/*
41 | ```
42 |
43 | on Linux if this directory (containing the `README.md`) is in `/var/www/html/` on the server.
44 | 1. Ensure that the configuration in the `config/` directory – which usually contains sensitive information such as database credentials, SMTP passwords or API tokens – can be read only by the web server. For example, execute
45 |
46 | ```
47 | $ sudo chown -R www-data:www-data /var/www/html/config
48 | $ sudo chmod 0555 /var/www/html/config
49 | $ sudo chmod 0400 /var/www/html/config/.env
50 | ```
51 |
52 | on Linux if this directory (containing the `README.md`) is in `/var/www/html/` on the server.
53 | 1. If you want to enable automatic backups in compressed and encrypted form, please refer to the [“Backups” section](#backups) further below.
54 |
55 | ## Usage
56 |
57 | * [Application structure](#application-structure)
58 | * [Routing](#routing)
59 | * [Charsets and encodings](#charsets-and-encodings)
60 | * [Storage](#storage)
61 | * [Database access](#database-access)
62 | * [String handling](#string-handling)
63 | * [Input validation](#input-validation)
64 | * [HTML escaping](#html-escaping)
65 | * [Templates](#templates)
66 | * [Authentication](#authentication)
67 | * [Sessions and cookies](#sessions-and-cookies)
68 | * [Flash messages](#flash-messages)
69 | * [Mail](#mail)
70 | * [Obfuscation of IDs](#obfuscation-of-ids)
71 | * [Uploading files](#uploading-files)
72 | * [Serving files](#serving-files)
73 | * [File downloads](#file-downloads)
74 | * [Internationalization (I18N) and localization (L10N)](#internationalization-i18n-and-localization-l10n)
75 | * [Helpers and utilities](#helpers-and-utilities)
76 |
77 | ### Application structure
78 |
79 | * `app/`: This is the most important directory: It’s where you’ll write all your PHP code. It’s entirely up to you how you structure your application in this directory. Create as many files and subdirectories here as you wish. The `index.php` file is the main entry point to your application. The complete folder is exclusively for you.
80 | * `backups/`: If you make use of the convenient `backup.sh` script, this is where the compressed and encrypted backups will be stored. See the [“Backups” section](#backups) further down.
81 | * `config/`: Store your local configuration, which should include all confidential information, keys and secrets, here. Put everything in `config/.env` while keeping an up-to-date copy of that file in `config/.env.example` which should have exactly the same keys but all the confidential values removed. The first file will be your private configuration, the second file can be checked in to version control for others to see what configuration keys they need. Always remember to securely back up the private configuration file (`config/.env`) somewhere outside of your VCS. Do *not* store it in version control.
82 | * `public/`: This is where you can store your static assets, such as CSS files, JavaScript files, images, your `robots.txt` and so on. The files will be available at the root URL of your application, i.e. `public/favicon.ico` can simply be accessed at `favicon.ico`. The complete folder is exclusively for you.
83 | * `storage/app/`: Storage that you can use in your app to store files temporarily or permanently. This space is private to your application. Feel free to add any number of subfolders and files here.
84 | * `storage/framework/`: Storage used by the framework, e.g. for caching. Do *not* add, modify or delete anything here.
85 | * `vendor/`: This directory is where Composer will install all dependencies. Do *not* add, modify or delete anything here.
86 | * `views/`: If you use templates for HTML, XML, CSV or LaTeX (or anything else) that should be rendered by the built-in template engine, this is where you should store them.
87 | * `.htaccess`: Rules and optimizations for Apache HTTP Server. You may add your own rules here, but only in the `CUSTOM` section at the bottom.
88 | * `backup.sh`: Script that can create compressed and encrypted backups in the `backups/` directory for you. See the [“Backups” section](#backups) further below.
89 | * `composer.json`: The dependencies of your application. This framework must *always* be included in the `require` section as `delight-im/framework`, so please don’t remove that entry. But otherwise, feel free to add or modify any of your own entries.
90 | * `index.php`: This is the main controller of this framework. It sets everything up correctly and passes on control to your application code. Do *not* change or delete this.
91 |
92 | ### Routing
93 |
94 | Your application will be available at the URL where this root folder is accessible on your web server.
95 |
96 | **Note:** You should have added this URL to the configuration in `config/.env` as `APP_PUBLIC_URL` already. This is required for your application to work correctly.
97 |
98 | Sometimes a single application is expected to respond to *multiple* hostnames with *different* content. Examples include the use of language-specific subdomains (such as `fr.example.com` and `id.example.com`), country-specific TLDs (such as `example.com` and `example.org`), separate storefronts (such as `seller-a.example.com` and `seller-b.example.com`), or user-specific content (such as `jane.example.org` and `john.example.org`). For those cases, you can define multiple supported URLs in `APP_PUBLIC_URL` by separating the individual URLs using the vertical bar or pipe character (`U+007C VERTICAL LINE`). In your application code, you should then decide which content to show based on the value of `$app->getCanonicalHost()`.
99 |
100 | However, if you just want to serve the *same* content at *multiple* hostnames or schemes, you should consider using redirects instead, which can forward from different hostnames or schemes to one canonical form. In most of these cases, you’ll want to send HTTP status `301` for *permanent* redirects.
101 |
102 | The single pages or endpoints of your application will be served at the routes that you define in the `app/` directory. A few sample routes have already been added.
103 |
104 | For more detailed information on routes, please refer to the [documentation of the router](https://github.com/delight-im/PHP-Router).
105 |
106 | ### Charsets and encodings
107 |
108 | Everything is UTF-8 all the way through! Just remember to save your own source files as UTF-8, too.
109 |
110 | ### Storage
111 |
112 | You may store files private to your application in `storage/app/`. Feel free to store the files either in the root directory or in any number of subfolders that you create.
113 |
114 | The storage path can be retrieved in your application code via `$app->getStoragePath('/path/to/subfolder/file.txt')`, for example.
115 |
116 | Files inside `storage/app/` are private to your application. You may use PHP’s file handling utilities to work with the files or offer them to the user as a download (see "File downloads" below).
117 |
118 | ### Database access
119 |
120 | Did you set your database credentials in `config/.env`? If the configuration is valid, getting a database connection is as simple as this:
121 |
122 | ```php
123 | $app->db();
124 | ```
125 |
126 | For information on how to read and write data using this instance, please refer to the [documentation of the database library](https://github.com/delight-im/PHP-DB).
127 |
128 | **Note:** You don’t have to construct the database instance or establish the connection yourself. This will be done for you automatically. Just call the methods to read and write data on the instance provided.
129 |
130 | ### String handling
131 |
132 | Convenient string handling in an object-oriented way with full Unicode support is available on any string after wrapping it in the `s(...)` helper function.
133 |
134 | For information on all the methods available with strings wrapped in `s(...)`, please refer to the [documentation of the string library](https://github.com/delight-im/PHP-Str).
135 |
136 | **Note:** There is no need for you to set up the `s(...)` shorthand anymore. This has been done already.
137 |
138 | ### Input validation
139 |
140 | This framework comes with easy and safe input validation for `GET`, `POST` and cookie data as well as for any existing variable.
141 |
142 | Both validation and filtering are performed automatically and *correctly typecast* values are returned for you.
143 |
144 | If a value does not exist or if it’s invalid, you’re guaranteed to receive `null` as the result. This way, you don’t have to check for empty strings, `null`, `false` or invalid formats separately.
145 |
146 | ```php
147 | $app->input()->get('username'); // equivalent to TYPE_STRING
148 | $app->input()->get('profileId', TYPE_INT);
149 | $app->input()->get('disabled', TYPE_BOOL);
150 | $app->input()->get('weight', TYPE_FLOAT);
151 | $app->input()->get('message', TYPE_TEXT);
152 | $app->input()->get('recipientEmail', TYPE_EMAIL);
153 | $app->input()->get('linkTo', TYPE_URL);
154 | $app->input()->get('whoisIp', TYPE_IP);
155 | $app->input()->get('transmission', TYPE_RAW);
156 |
157 | $app->input()->post('country'); // equivalent to TYPE_STRING
158 | $app->input()->post('zipCode', TYPE_INT);
159 | $app->input()->post('subscribe', TYPE_BOOL);
160 | $app->input()->post('price', TYPE_FLOAT);
161 | $app->input()->post('csv', TYPE_TEXT);
162 | $app->input()->post('newsletterEmail', TYPE_EMAIL);
163 | $app->input()->post('referringSite', TYPE_URL);
164 | $app->input()->post('signUpIp', TYPE_IP);
165 | $app->input()->post('print', TYPE_RAW);
166 |
167 | $app->input()->cookie('realName'); // equivalent to TYPE_STRING
168 | $app->input()->cookie('lastLoginTimestamp', TYPE_INT);
169 | $app->input()->cookie('hideAds', TYPE_BOOL);
170 | $app->input()->cookie('cartTotalAmount', TYPE_FLOAT);
171 | $app->input()->cookie('draft', TYPE_TEXT);
172 | $app->input()->cookie('friend', TYPE_EMAIL);
173 | $app->input()->cookie('social', TYPE_URL);
174 | $app->input()->cookie('antiFraud', TYPE_IP);
175 | $app->input()->cookie('display', TYPE_RAW);
176 |
177 | $app->input()->value($username); // equivalent to TYPE_STRING
178 | $app->input()->value($profileId, TYPE_INT);
179 | $app->input()->value($disabled, TYPE_BOOL);
180 | $app->input()->value($weight, TYPE_FLOAT);
181 | $app->input()->value($message, TYPE_TEXT);
182 | $app->input()->value($recipientEmail, TYPE_EMAIL);
183 | $app->input()->value($linkTo, TYPE_URL);
184 | $app->input()->value($whoisIp, TYPE_IP);
185 | $app->input()->value($transmission, TYPE_RAW);
186 | ```
187 |
188 | ### HTML escaping
189 |
190 | In order to escape any string for safe use in HTML, just wrap the string in the `e(...)` helper function:
191 |
192 | ```php
193 | echo e('Bob says "Hello world"');
194 | // => Bob <b>says</b> "Hello world"
195 |
196 | // or
197 |
198 | echo '
';
199 | ```
200 |
201 | ```html
202 |
203 |
204 |
205 | ```
206 |
207 | If you use templates stored in the `views/` directory (see below), you don’t need this, as templates come with automatic escaping by default.
208 |
209 | ### Templates
210 |
211 | Place your HTML, XML, CSV or LaTeX (or anything else) templates in the `views/` folder. Make them powerful and re-usable with the [Twig language](http://twig.sensiolabs.org/doc/templates.html). A few examples for such templates have already been added.
212 |
213 | In order to render a template, just load it in your PHP code:
214 |
215 | ```php
216 | echo $app->view('welcome.html.twig');
217 | ```
218 |
219 | Usually, you’ll want to pass data to the templates as well:
220 |
221 | ```php
222 | echo $app->view('welcome.html.twig', [
223 | 'userId' => $id
224 | 'name' => $name
225 | 'messages' => $messages
226 | ]);
227 | ```
228 |
229 | The data that you passed in the second parameter of the `$app->view(...)` method will be available in your template:
230 |
231 | ```html
232 |
Hello, {{ name }}!
233 | ```
234 |
235 | All output is escaped (and thus safe) by default! If you need to disable escaping for a variable, you can do so using the `raw` filter:
236 |
237 | ```html
238 | Goodbye, {{ name | raw }}!
239 | ```
240 |
241 | Conditional expressions and loops are available as control structures:
242 |
243 | ```html
244 | {% if messages|length > 0 %}
245 | You have {{ messages|length }} new messages!
246 | {% endif %}
247 | ```
248 |
249 | ```html
250 |
251 | {% for picture in pictures %}
252 |

253 | {% endfor %}
254 |
255 | ```
256 |
257 | For less code and more markup re-use, templates can be embedded inside each other:
258 |
259 | ```html
260 | {% include 'includes/header.html.twig' %}
261 | ```
262 |
263 | Embedding can even be combined with loops:
264 |
265 | ```html
266 |
267 | {% for user in users %}
268 | - {{ user.username }}
269 | {% for picture in user.pictures %}
270 | {% include 'picture_box.html.twig' %}
271 |
272 | {% endfor %}
273 | {% endfor %}
274 |
275 | ```
276 |
277 | You can add custom filters in your PHP code which can then be used to modify data in the templates:
278 |
279 | ```php
280 | $app->getTemplateManager()->addFilter('repeatAndReverse', function ($str) {
281 | return strrev($str.' '.$str);
282 | });
283 | ```
284 |
285 | ```html
286 | We will repeat {{ myVariable | repeatAndReverse }} and reverse this!
287 | ```
288 |
289 | Moreover, you can add variables as globals in your PHP code which can then be accessed in the templates:
290 |
291 | ```php
292 | $app->getTemplateManager()->addGlobal('googleAnalytics', $ga);
293 | ```
294 |
295 | ```html
296 | {{ googleAnalytics.trackingCode | raw }}
297 |