├── .github ├── FUNDING.yml └── workflows │ ├── code-style.yml │ └── publish-to-redaxo-org.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── FUNDING.yml ├── LICENSE ├── README.md ├── boot.php ├── composer.json ├── fragments └── dummy-fragment.php ├── install.php ├── install └── rex_cronjob_yform_auto_delete.sql ├── lang ├── de_de.lang └── en_gb.lang ├── lib ├── auto_delete.php ├── folder_auto_delete.cronjob.php ├── table_auto_delete.cronjob.php ├── yform │ └── value │ │ └── datestamp_auto_delete.php └── yform_auto_delete.cronjob.php ├── package.yml ├── pages └── index.php ├── uninstall.php └── update.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: alxndr-w 2 | ko_fi: alxndr-w 3 | custom: ['https://www.alexplus.de'] 4 | -------------------------------------------------------------------------------- /.github/workflows/code-style.yml: -------------------------------------------------------------------------------- 1 | name: PHP-CS-Fixer 2 | 3 | on: 4 | push: 5 | branches: [ master, main ] 6 | pull_request: 7 | branches: [ master, main ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | code-style: 14 | 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: write # for Git to git apply 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - name: Setup PHP 23 | uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: '8.3' 26 | extensions: gd, intl, pdo_mysql 27 | coverage: none # disable xdebug, pcov 28 | 29 | # install dependencies from composer.json 30 | - name: Install test dependencies 31 | env: 32 | COMPOSER: composer.json 33 | run: composer install --prefer-dist --no-progress 34 | 35 | # run php-cs-fixer, fix code styles 36 | - name: Run PHP CS Fixer 37 | run: composer cs-fix 38 | 39 | # commit and push fixed files 40 | - uses: stefanzweifel/git-auto-commit-action@v4 41 | with: 42 | commit_message: Apply php-cs-fixer changes 43 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-redaxo-org.yml: -------------------------------------------------------------------------------- 1 | # Instructions: https://github.com/FriendsOfREDAXO/installer-action/ 2 | 3 | name: Publish to REDAXO.org 4 | on: 5 | release: 6 | types: 7 | - published 8 | 9 | jobs: 10 | redaxo_publish: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: FriendsOfREDAXO/installer-action@v1 15 | with: 16 | myredaxo-username: ${{ secrets.MYREDAXO_USERNAME }} 17 | myredaxo-api-key: ${{ secrets.MYREDAXO_API_KEY }} 18 | description: ${{ github.event.release.body }} 19 | version: ${{ github.event.release.tag_name }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /composer.lock 3 | /.php-cs-fixer.cache 4 | /vendor 5 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in(__DIR__) 7 | ; 8 | 9 | return (new Redaxo\PhpCsFixerConfig\Config()) 10 | ->setFinder($finder) 11 | ; 12 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: alxndr-w 2 | ko_fi: alxndr-w 3 | custom: ['https://www.alexplus.de'] 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 alex+ Informationsdesign 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 | 2 | # Auto-Delete-Addon für REDAXO 5.x 3 | 4 | ![image](https://user-images.githubusercontent.com/3855487/152675689-328899a4-90d6-41da-bef4-78d1c8e7f8c5.png) 5 | 6 | Löscht alte Logs und Datensätze via Cronjob. 7 | 8 | ## Features 9 | 10 | ### YForm-Feld `datestamp_auto_delete` 11 | 12 | Ein Feld für YForm, das wie `datestamp` beim Erstellen oder Updaten eines Datensatzes einen Zeitstempel in der Zukunft erzeugt, der als Referenz für den passenden Lösch-Cronjob herangezogen wird. 13 | 14 | [Liste der möglichen Offset-Parameter in den PHP-Docs](https://www.php.net/manual/de/function.strtotime.php) 15 | 16 | ### Cronjob `yform_auto_delete` 17 | 18 | Das passende Gegenstück zu `datestamp_auto_delete` für YForm. Durchsucht alle in YForm verwalteten Tabellen nach dem Feld `datestamp_auto_delete` und löscht den Datensatz auf Basis von YOrm unter Berücksichtigung der jeweiligen Extension Points. 19 | 20 | ### Cronjob `folder_auto_delete` 21 | 22 | Durchsucht ein angegebenes Verzeichnis nach Daten, die älterer sind als ein gewünschter Zeitpunkt und löscht diese. Vergleichbar mit dem von REDAXO mitgeliefertem Cronjob für PHPMailer-Logs oder Datenbank-Sicherungen, jedoch für ein frei wählbares Verzeichnis. 23 | 24 | Z.B. Ordner, in die der Nutzer über YForm im Frontend Daten hochlädt, denkbar bei Bewerberformularen. 25 | 26 | > **Vorsicht:** Falsch angegebene Pfade können zu ungewolltem Datenverlust führen. Bitte die Pfadangaben vorher überprüfen. 27 | 28 | ### Cronjob `table_auto_delete` 29 | 30 | > **Hinweis:** nur empfohlen für Tabellen, die **nicht** in YForm verwaltet werden. 31 | 32 | Ein Cronjob, der ein beliebiges Feld einer beliebigen Tabelle heranzieht (z.B. `updatedate`) und nach einem festgelegten Zeitabstand, z.B. `+ 3 months` die Daten darin löscht. 33 | 34 | ## Lizenz 35 | 36 | MIT Lizenz, siehe [LICENSE.md](https://github.com/alexplusde/auto_delete/blob/master/LICENSE.md) 37 | 38 | ## Autoren 39 | 40 | **Alexander Walther** 41 | 42 | 43 | 44 | **Projekt-Lead** 45 | [Alexander Walther](https://github.com/alexplusde) 46 | -------------------------------------------------------------------------------- /boot.php: -------------------------------------------------------------------------------- 1 | isAvailable() && !rex::isSafeMode()) { 4 | rex_cronjob_manager::registerType('rex_cronjob_table_auto_delete'); 5 | rex_cronjob_manager::registerType('rex_cronjob_yform_auto_delete'); 6 | rex_cronjob_manager::registerType('rex_cronjob_folder_auto_delete'); 7 | } 8 | 9 | // auto_delete::writeCronjob(); 10 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "redaxo/php-cs-fixer-config": "^2.0", 4 | "friendsofphp/php-cs-fixer": "^3.14" 5 | }, 6 | "replace": { 7 | "psr/log": "*", 8 | "psr/container": "*" 9 | }, 10 | "scripts": { 11 | "cs-dry": "php-cs-fixer fix -v --ansi --dry-run --config=.php-cs-fixer.dist.php", 12 | "cs-fix": "php-cs-fixer fix -v --ansi --config=.php-cs-fixer.dist.php" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /fragments/dummy-fragment.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexplusde/auto_delete/061c2a29a0f8a4bee941d7ac032c47bae71488eb/fragments/dummy-fragment.php -------------------------------------------------------------------------------- /install.php: -------------------------------------------------------------------------------- 1 | isAvailable()) { 5 | $cronjob = array_filter(rex_sql::factory()->getArray("SELECT * FROM rex_cronjob WHERE `type` = 'rex_cronjob_neues_publish'")); 6 | if (!$cronjob) { 7 | $query = rex_file::get(__DIR__ . '/install/rex_cronjob_yform_auto_delete.sql'); 8 | rex_sql::factory()->setQuery($query); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /install/rex_cronjob_yform_auto_delete.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `rex_cronjob` 2 | (`name`, `description`, `type`, `parameters`, `interval`, `nexttime`, `environment`, `execution_moment`, `execution_start`, `status`, `createdate`, `createuser`, `updatedate`, `updateuser`) 3 | VALUES 4 | ('Add-on Auto Delete: YForm', 'Automatisch erstellter Cronjob durch das Add-On auto_delete, das entsprechende Felder in YForm und der Datenbank durchsucht und automatisch löscht.', 'rex_cronjob_yform_auto_delete', '[]', '{"minutes":"all","hours":[0],"days":[1],"weekdays":"all","months":[1]}', NOW(), '|frontend|backend|script|', 1, '0000-00-00 00:00:00', 0, NOW(), 'auto_delete', NOW(), 'auto_delete'); 5 | -------------------------------------------------------------------------------- /lang/de_de.lang: -------------------------------------------------------------------------------- 1 | auto_delete_title: Automatisches Löschen 2 | 3 | 4 | # YForm Tablelen-Übersetzung 5 | 6 | # YForm Feld 7 | yform_values_datestamp_auto_delete_offset = Löschen nach... 8 | yform_values_datestamp_auto_delete_offset_notice =in PHP-Format strtotime(), z.B. + 6 months oder next Monday oder 31 December 2025. Siehe PHP-Docs 9 | yform_values_datestamp_auto_delete_description = 🧩 Auto Delete: Ein datestamp-Feld, mit einem Lösch-Datum in der Zukunft. 10 | 11 | auto_delete_yform = Lösche YForm-Datensätze anhand des Felds datestamp_auto_delete 12 | auto_delete_yform_cronjob_message = Datensätze wurden erfolgreich gelöscht. 13 | 14 | auto_delete_folder = Lösche Dateien in einem gewählten Verzeichnis 15 | auto_delete_folder_days_label = Lösche Dateien nach einer bestimmten Zeit 16 | auto_delete_folder_days = Tage 17 | auto_delete_folder_label = Absoluter Ordner-Pfad 18 | 19 | auto_delete_table = Lösche Datensätze in einer gewählten Datenbank-Tabelle nach einer bestimmten Zeit 20 | auto_delete_table_cronjob_rex_table_label = Name der Tabelle 21 | auto_delete_table_cronjob_rex_table_notice = z.B. rex_article_history 22 | auto_delete_table_cronjob_field_label = Feld 23 | auto_delete_table_cronjob_field_notice = z.B. createdate 24 | auto_delete_table_cronjob_interval_label = Offset 25 | #auto_delete_table_cronjob_interval_notice = Nach wie vielen Monaten, z.B. 6 für "6 Monate später". 26 | -------------------------------------------------------------------------------- /lang/en_gb.lang: -------------------------------------------------------------------------------- 1 | auto_delete_title: Automatisches Löschen 2 | 3 | 4 | # YForm Tablelen-Übersetzung 5 | 6 | # YForm Feld 7 | yform_values_datestamp_auto_delete_offset = Löschen nach... 8 | yform_values_datestamp_auto_delete_offset_notice = In PHP-Format strtotime(), z.B. + 6 months oder next Monday oder 31 December 2025. Siehe PHP-Docs 9 | yform_values_datestamp_auto_delete_description = Ein datestamp-Feld, mit einem Lösch-Datum in der Zukunft. 10 | 11 | auto_delete_yform = Lösche YForm-Datensätze anhand des Felds datestamp_auto_delete 12 | auto_delete_yform_cronjob_message = Datensätze wurden erfolgreich gelöscht. 13 | 14 | auto_delete_folder = Lösche Dateien in einem gewählten Verzeichnis 15 | auto_delete_folder_days_label 16 | auto_delete_folder_days = Tage 17 | auto_delete_folder_label = Absoluter Ordner-Pfad 18 | 19 | auto_delete_table = Lösche Datensätze in einer gewählten Datenbank-Tabelle nach einer bestimmten Zeit 20 | auto_delete_table_cronjob_rex_table_label = Name der Tabelle 21 | auto_delete_table_cronjob_rex_table_notice = z.B. rex_article_history 22 | auto_delete_table_cronjob_field_label = Feld 23 | auto_delete_table_cronjob_field_notice = z.B. createdate 24 | auto_delete_table_cronjob_interval_label = Offset 25 | #auto_delete_table_cronjob_interval_notice = Nach wie vielen Monaten, z.B. 6 für "6 Monate später". 26 | -------------------------------------------------------------------------------- /lib/auto_delete.php: -------------------------------------------------------------------------------- 1 | getArray('SELECT * FROM `' . rex::getTable('yform_field') . '` WHERE `type_name` = "datestamp_auto_delete" ') as $field) { 8 | rex_sql::factory()->setQuery('DELETE FROM ' . $field['table_name'] . ' WHERE ' . $field['name'] . ' < NOW()'); 9 | } 10 | } 11 | 12 | public static function writeCronjob() 13 | { 14 | $cronjobs = rex_sql::factory()->setDebug(0)->getArray("SELECT * FROM rex_cronjob WHERE `type` LIKE '%_auto_delete'"); 15 | 16 | foreach ($cronjobs as $cronjob) { 17 | rex_file::put(rex_path::addon('auto_delete', 'cronjob/' . $cronjob['type'] . '.json'), json_encode($cronjob)); 18 | } 19 | } 20 | 21 | public static function updateCronjob() 22 | { 23 | $cronjobs = scandir(rex_path::addon('auto_delete') . 'cronjob'); 24 | 25 | foreach ($cronjobs as $cronjob) { 26 | if ('.' == $cronjob || '..' == $cronjob) { 27 | continue; 28 | } 29 | $cronjob_array = json_decode(rex_file::get(rex_path::addon('auto_delete') . 'cronjob/' . $cronjob), 1); 30 | 31 | rex_sql::factory()->setDebug(0)->setTable('rex_cronjob') 32 | ->setTable(rex::getTable('cronjob')) 33 | ->setValue('name', $cronjob_array['name']) 34 | ->setValue('description', $cronjob_array['description']) 35 | ->setValue('type', $cronjob_array['type']) 36 | ->setValue('interval', $cronjob_array['interval']) 37 | ->setValue('environment', $cronjob_array['environment']) 38 | ->setValue('execution_start', '1970-01-01 01:00:00') 39 | ->setValue('status', '1') 40 | ->setValue('parameters', $cronjob_array['parameters']) 41 | ->setValue('nexttime', $cronjob_array['nexttime']) 42 | ->setValue('createdate', '') 43 | ->setValue('updatedate', '') 44 | ->setValue('createuser', 'auto_delete') 45 | ->setValue('updateuser', 'auto_delete') 46 | ->insertOrUpdate(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/folder_auto_delete.cronjob.php: -------------------------------------------------------------------------------- 1 | (60 * 60 * 24 * $days)) { 14 | if (rex_file::delete($file)) { 15 | ++$log; 16 | } 17 | } 18 | } 19 | if ('' != $dir && $dir != rex_mailer::logFolder() && is_dir($dir) && 0 === count(glob("$dir/*"))) { 20 | if (true == rmdir($dir)) { 21 | } 22 | } 23 | } 24 | return $log; 25 | } 26 | 27 | public function execute() 28 | { 29 | $dir = $this->getParam('dir'); 30 | if ('' != $dir && is_dir($dir)) { 31 | $days = (int) $this->getParam('days'); 32 | $purgeLog = self::purgeDir($days, $dir); 33 | if (0 != $purgeLog) { 34 | $this->setMessage('Files deleted: ' . $purgeLog); 35 | return true; 36 | } 37 | $this->setMessage('No files found to delete'); 38 | return true; 39 | } 40 | $this->setMessage('Unable to find folder'); 41 | return false; 42 | } 43 | 44 | public function getTypeName() 45 | { 46 | return rex_i18n::msg('auto_delete_folder'); 47 | } 48 | 49 | public function getParamFields() 50 | { 51 | $folders = []; 52 | 53 | foreach ($iterator = new RecursiveIteratorIterator( 54 | new RecursiveDirectoryIterator( 55 | rex_path::data(), 56 | RecursiveDirectoryIterator::SKIP_DOTS, 57 | ), 58 | RecursiveIteratorIterator::SELF_FIRST, 59 | ) as $item) { 60 | // Note SELF_FIRST, so array keys are in place before values are pushed. 61 | 62 | $subPath = $iterator->getSubPathName(); 63 | if ($item->isDir()) { 64 | // Create a new array key of the current directory name. 65 | $folders[rex_path::data() . $subPath] = rex_path::data() . $subPath; 66 | } 67 | } 68 | 69 | foreach ($iterator = new RecursiveIteratorIterator( 70 | new RecursiveDirectoryIterator( 71 | rex_path::cache(), 72 | RecursiveDirectoryIterator::SKIP_DOTS, 73 | ), 74 | RecursiveIteratorIterator::SELF_FIRST, 75 | ) as $item) { 76 | // Note SELF_FIRST, so array keys are in place before values are pushed. 77 | 78 | $subPath = $iterator->getSubPathName(); 79 | if ($item->isDir()) { 80 | // Create a new array key of the current directory name. 81 | $folders[rex_path::cache() . $subPath] = rex_path::cache() . $subPath; 82 | } 83 | } 84 | 85 | return [ 86 | [ 87 | 'label' => rex_i18n::msg('auto_delete_folder_days_label'), 88 | 'name' => 'days', 89 | 'type' => 'select', 90 | 'options' => [ 91 | 7 => '7 ' . rex_i18n::msg('auto_delete_folder_days'), 92 | 14 => '14 ' . rex_i18n::msg('auto_delete_folder_days'), 93 | 30 => '30 ' . rex_i18n::msg('auto_delete_folder_days'), 94 | 90 => '90 ' . rex_i18n::msg('auto_delete_folder_days'), 95 | 180 => '180 ' . rex_i18n::msg('auto_delete_folder_days'), 96 | ], 97 | 'default' => 7, 98 | ], [ 99 | 'label' => rex_i18n::msg('auto_delete_folder_label'), 100 | 'name' => 'dir', 101 | 'type' => 'select', 102 | 'attributes' => ['class' => 'form-control selectpicker'], 103 | 'options' => $folders, 104 | 'default' => 7, 105 | 'notice' => rex_path::data(), 106 | ], 107 | ]; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/table_auto_delete.cronjob.php: -------------------------------------------------------------------------------- 1 | query('DELETE FROM ' . $this->getParam('rex_table') . ' WHERE ' . (string) $this->getParam('field') . ' < MONTH(NOW() - INTERVAL ' . (int) $this->getParam('interval') . ' MONTH)'); 8 | 9 | $this->setMessage('Datensätze in der Tabelle ' . $this->getParam('rex_table') . ' gelöscht, die älter als ' . $this->getParam('interval') . ' Monate waren.'); 10 | return true; 11 | } 12 | 13 | public function getTypeName() 14 | { 15 | return rex_i18n::msg('auto_delete_table'); 16 | } 17 | 18 | public function getParamFields() 19 | { 20 | // Eingabefelder des Cronjobs definieren 21 | $fields = [ 22 | [ 23 | 'label' => rex_i18n::msg('auto_delete_table_cronjob_rex_table_label'), 24 | 'name' => 'rex_table', 25 | 'type' => 'select', 26 | 'options' => array_column(rex_sql::factory()->getArray('SELECT TABLE_NAME as id, TABLE_NAME as name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE "rex_%"'), 'id', 'name'), 27 | 'notice' => rex_i18n::msg('auto_delete_table_cronjob_rex_table_notice'), 28 | ], 29 | [ 30 | 'label' => rex_i18n::msg('auto_delete_table_cronjob_field_label'), 31 | 'name' => 'field', 32 | 'type' => 'text', 33 | 'default' => 'createdate', 34 | 'notice' => rex_i18n::msg('auto_delete_table_cronjob_field_notice'), 35 | ], 36 | [ 37 | 'label' => rex_i18n::msg('auto_delete_table_cronjob_interval_label'), 38 | 'name' => 'interval', 39 | 'default' => '6', 40 | 'type' => 'text', 41 | 'notice' => rex_i18n::msg('auto_delete_table_cronjob_interval_notice'), 42 | ], 43 | ]; 44 | 45 | return $fields; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/yform/value/datestamp_auto_delete.php: -------------------------------------------------------------------------------- 1 | getValue() . ' ' . $this->getElement('offset'))); 14 | 15 | $this->setValue($value); 16 | } 17 | 18 | public function getDefinitions(): array 19 | { 20 | return [ 21 | 'type' => 'value', 22 | 'name' => 'datestamp_auto_delete', 23 | 'values' => [ 24 | 'name' => ['type' => 'name', 'label' => rex_i18n::msg('yform_values_defaults_name')], 25 | 'label' => ['type' => 'text', 'label' => rex_i18n::msg('yform_values_defaults_label')], 26 | 'format' => ['type' => 'choice', 'label' => rex_i18n::msg('yform_values_datetime_format'), 'choices' => rex_yform_value_datetime::VALUE_DATETIME_FORMATS, 'default' => rex_yform_value_datetime::VALUE_DATETIME_DEFAULT_FORMAT], 27 | 'no_db' => ['type' => 'no_db', 'label' => rex_i18n::msg('yform_values_defaults_table'), 'default' => 0], 28 | 'only_empty' => ['type' => 'choice', 'label' => rex_i18n::msg('yform_values_datestamp_only_empty'), 'default' => '0', 'choices' => 'translate:yform_always=0,translate:yform_onlyifempty=1,translate:yform_never=2'], 29 | 'offset' => ['type' => 'text', 'label' => rex_i18n::msg('yform_values_datestamp_auto_delete_offset'), 'notice' => rex_i18n::msg('yform_values_datestamp_auto_delete_offset_notice'), 'default' => '+6 months'], 30 | ], 31 | 'description' => rex_i18n::msg('yform_values_datestamp_auto_delete_description'), 32 | 'db_type' => ['datetime'], 33 | 'multi_edit' => false, 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/yform_auto_delete.cronjob.php: -------------------------------------------------------------------------------- 1 | 7.3,<9' 10 | redaxo: ^5.13 11 | packages: 12 | yform: "^4" 13 | cronjob: "^2.8" 14 | 15 | page: 16 | title: translate:auto_delete_title 17 | prio: 100 18 | icon: rex-icon fa-gear 19 | hidden: true 20 | -------------------------------------------------------------------------------- /pages/index.php: -------------------------------------------------------------------------------- 1 | includeFile(__DIR__ . '/install.php'); 5 | --------------------------------------------------------------------------------