diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | composer.json | 2 | ||||
-rw-r--r-- | composer.lock | 186 | ||||
-rw-r--r-- | config.example.toml | 3 | ||||
-rw-r--r-- | config.example.yaml | 3 | ||||
-rw-r--r-- | public/index.php | 8 | ||||
-rw-r--r-- | src/App.php | 19 | ||||
-rw-r--r-- | src/Builder.php | 35 | ||||
-rw-r--r-- | src/Controllers/EntriesController.php | 4 | ||||
-rw-r--r-- | src/Controllers/SubmissionController.php | 24 | ||||
-rw-r--r-- | src/Form.php | 22 | ||||
-rw-r--r-- | src/HookManager.php | 4 | ||||
-rw-r--r-- | src/Validator.php | 18 | ||||
-rw-r--r-- | tests/Test.php | 180 |
14 files changed, 267 insertions, 243 deletions
@@ -1,5 +1,5 @@ /vendor/ /content/ /plugins/ -/config.toml +/config.yaml diff --git a/composer.json b/composer.json index 2f4c889..88fb362 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "require": { "symfony/http-foundation": "^6.0", "twig/twig": "^3.3", - "yosymfony/toml": "^1.0" + "symfony/yaml": "^6.0" }, "require-dev": { "phpunit/phpunit": "^9.5", diff --git a/composer.lock b/composer.lock index b76bbec..fb703e6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2c5ed920e00cd871d918787e2aad9fa0", + "content-hash": "8714b8c07f79460ddb0d4a15a753714c", "packages": [ { "name": "symfony/deprecation-contracts", @@ -305,6 +305,80 @@ "time": "2021-05-27T12:26:48+00:00" }, { + "name": "symfony/yaml", + "version": "v6.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "e77f3ea0b21141d771d4a5655faa54f692b34af5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e77f3ea0b21141d771d4a5655faa54f692b34af5", + "reference": "e77f3ea0b21141d771d4a5655faa54f692b34af5", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v6.0.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-26T17:23:29+00:00" + }, + { "name": "twig/twig", "version": "v3.3.4", "source": { @@ -379,116 +453,6 @@ } ], "time": "2021-11-25T13:46:55+00:00" - }, - { - "name": "yosymfony/parser-utils", - "version": "v2.0.0", - "source": { - "type": "git", - "url": "https://github.com/yosymfony/parser-utils.git", - "reference": "00bec9a12722b21f2baf7f9db35f127e90c162c9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/yosymfony/parser-utils/zipball/00bec9a12722b21f2baf7f9db35f127e90c162c9", - "reference": "00bec9a12722b21f2baf7f9db35f127e90c162c9", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "Yosymfony\\ParserUtils\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Victor Puertas", - "email": "vpgugr@gmail.com", - "homepage": "http://yosymfony.com" - } - ], - "description": "Parser utilities", - "homepage": "http://github.com/yosymfony/toml", - "keywords": [ - "lexer", - "parser" - ], - "support": { - "issues": "https://github.com/yosymfony/parser-utils/issues", - "source": "https://github.com/yosymfony/parser-utils/tree/master" - }, - "time": "2018-06-29T15:31:11+00:00" - }, - { - "name": "yosymfony/toml", - "version": "v1.0.4", - "source": { - "type": "git", - "url": "https://github.com/yosymfony/toml.git", - "reference": "bdab92ad920d0e36810a3a3e4a998d23f3498f8e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/yosymfony/toml/zipball/bdab92ad920d0e36810a3a3e4a998d23f3498f8e", - "reference": "bdab92ad920d0e36810a3a3e4a998d23f3498f8e", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "yosymfony/parser-utils": "^2.0" - }, - "require-dev": { - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Yosymfony\\Toml\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Victor Puertas", - "email": "vpgugr@gmail.com", - "homepage": "http://yosymfony.com" - } - ], - "description": "A PHP parser for TOML compatible with specification 0.4.0", - "homepage": "http://github.com/yosymfony/toml", - "keywords": [ - "mojombo", - "parser", - "toml" - ], - "support": { - "issues": "https://github.com/yosymfony/toml/issues", - "source": "https://github.com/yosymfony/toml/tree/master" - }, - "time": "2018-08-08T15:08:14+00:00" } ], "packages-dev": [ diff --git a/config.example.toml b/config.example.toml deleted file mode 100644 index 7d589d1..0000000 --- a/config.example.toml +++ /dev/null @@ -1,3 +0,0 @@ -[app] -contentFolderPath = './content' -pluginsFolderPath = './plugins' diff --git a/config.example.yaml b/config.example.yaml new file mode 100644 index 0000000..b19b35a --- /dev/null +++ b/config.example.yaml @@ -0,0 +1,3 @@ +app: + contentFolderPath: ./content + pluginsFolderPath: ./plugins diff --git a/public/index.php b/public/index.php index 96332f7..afa0afa 100644 --- a/public/index.php +++ b/public/index.php @@ -3,7 +3,7 @@ use FlatFileForms\App; use FlatFileForms\PluginLoader; use FlatFileForms\PreLoader; -use Yosymfony\Toml\Toml; +use Symfony\Component\Yaml\Yaml; require_once dirname(__DIR__) . '/vendor/autoload.php'; @@ -14,7 +14,7 @@ function findAppConfigFile(string $path): string { $currentDirectory = $path; while ($currentDirectory !== '/') { - $configFile = $currentDirectory . '/config.toml'; + $configFile = $currentDirectory . '/config.yaml'; if (file_exists($configFile)) { return $configFile; } @@ -22,12 +22,12 @@ function findAppConfigFile(string $path): string $currentDirectory = dirname($currentDirectory); } - die('config.toml missing'); + die('config.yaml missing'); } // find and parse config $configFile = findAppConfigFile(dirname(__DIR__)); -$config = Toml::parseFile($configFile); +$config = Yaml::parseFile($configFile); // prepare possibly relative folder paths chdir(dirname($configFile)); diff --git a/src/App.php b/src/App.php index 5fc62fa..ea9aa29 100644 --- a/src/App.php +++ b/src/App.php @@ -8,8 +8,7 @@ use FlatFileForms\Controllers\SubmissionController; use FlatFileForms\Controllers\ValidationController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Yosymfony\Toml\Toml; -use Yosymfony\Toml\TomlBuilder; +use Symfony\Component\Yaml\Yaml; class App { @@ -23,8 +22,13 @@ class App /**@var HookManager $hooks*/ global $hooks; + /**@var Form $form*/ + global $form; + $hooks->doAction('init'); + global $request; + global $response; $request = Request::createFromGlobals(); $response = new Response(); @@ -36,6 +40,7 @@ class App $method = $request->getMethod(); $path = $request->getPathInfo(); + $config = []; try { $config = $this->buildConfig($contentRoot . $path); @@ -52,6 +57,7 @@ class App if ($method == 'GET') { if (str_ends_with($path, '/fields')) { $formPath = $contentRoot . str_replace('/fields', '', $path); + $form = new Form($formPath); $builder = new Builder($formPath); @@ -66,6 +72,7 @@ class App } $formPath = $contentRoot . str_replace('/entries', '', $path); + $form = new Form($formPath); $entriesController = new EntriesController(); @@ -73,7 +80,7 @@ class App } else { - $content['data'] = Toml::parseFile($contentRoot . $path . '.toml'); + $content['data'] = Yaml::parseFile($contentRoot . $path . '.yaml'); } } @@ -81,6 +88,7 @@ class App else if ($method == 'POST') { if (str_ends_with($path, '/validate')) { $formPath = $contentRoot . str_replace('/validate', '', $path); + $form = new Form($formPath); $builder = new Builder($formPath); $validator = new Validator($formPath); @@ -92,6 +100,7 @@ class App else if (str_ends_with($path, '/submit')) { $formPath = $contentRoot . str_replace('/submit', '', $path); + $form = new Form($formPath); $builder = new Builder($formPath); $validator = new Validator($formPath); @@ -124,9 +133,9 @@ class App $config = []; $currentDirectory = $requestPath; while (true) { - $configFile = $currentDirectory . '/config/config.toml'; + $configFile = $currentDirectory . '/config/config.yaml'; if (file_exists($configFile)) { - $parsedConfig = Toml::parseFile($configFile); + $parsedConfig = Yaml::parseFile($configFile); $apiKeys = array_merge($parsedConfig['api']['keys'] ?? [], $config['api']['keys'] ?? []); $config = array_replace_recursive($parsedConfig, $config); diff --git a/src/Builder.php b/src/Builder.php index 308fa82..1172fc8 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -2,7 +2,7 @@ namespace FlatFileForms; -use Yosymfony\Toml\Toml; +use Symfony\Component\Yaml\Yaml; class Builder { @@ -13,27 +13,33 @@ class Builder public function buildFields(mixed $page = null) { - $parsed = Toml::parseFile($this->formPath . '/fields/_fields.toml'); + /**@var HookManager $hooks*/ + global $hooks; + + /**@var Form $form*/ + global $form; + + $parsed = Yaml::parseFile($this->formPath . '/fields/_fields.yaml'); $fields = []; // if a page is requested if ($page) { - if (! isset($parsed['page'])) { + if (! isset($parsed['pages'])) { throw new \Exception('Form has no pages'); } - if (! isset($parsed['page'][$page])) { + if (! isset($parsed['pages'][$page])) { throw new \Exception('Form has no page ' . $page); } - $fields = $this->buildSinglePageFields($parsed['page'][$page]); + $fields = $this->buildSinglePageFields($parsed['pages'][$page]); } // else get all fields else { // if form is paged - if (isset($parsed['page'])) { - $pages = $parsed['page']; + if (isset($parsed['pages'])) { + $pages = $parsed['pages']; foreach ($pages as $pageKey => $pageFields) { $fields[$pageKey] = $this->buildSinglePageFields($pageFields); } @@ -41,34 +47,37 @@ class Builder // if form is not paged else { - foreach ($parsed['field'] as $key => $field) { + foreach ($parsed as $key => $field) { $fields[$key] = $this->buildSingleField($key, $field); } } } + $fields = $hooks->applyFilter("builder:{$form->name}:fields", $fields); + $fields = $hooks->applyFilter('builder:fields', $fields); + return $fields; } - public function buildSinglePageFields(array $pageFields): array + private function buildSinglePageFields(array $pageFields): array { $fields = []; if (! empty($pageFields['file'])) { - $pageFields = array_replace_recursive($pageFields, Toml::parseFile($this->formPath . '/fields/' . $pageFields['file'])); + $pageFields = array_replace_recursive($pageFields, Yaml::parseFile($this->formPath . '/fields/' . $pageFields['file'])); } - foreach ($pageFields['field'] as $key => $field) { + foreach ($pageFields['fields'] as $key => $field) { $fields[$key] = $this->buildSingleField($key, $field); } return $fields; } - public function buildSingleField(string $key, array $field): array + private function buildSingleField(string $key, array $field): array { if (! empty($field['file'])) { - $field = array_replace_recursive($field, Toml::parseFile($this->formPath . '/fields/' . $field['file'])); + $field = array_replace_recursive($field, Yaml::parseFile($this->formPath . '/fields/' . $field['file'])); } if (empty($field['name'])) { diff --git a/src/Controllers/EntriesController.php b/src/Controllers/EntriesController.php index 801fe7b..f45866a 100644 --- a/src/Controllers/EntriesController.php +++ b/src/Controllers/EntriesController.php @@ -2,7 +2,7 @@ namespace FlatFileForms\Controllers; -use Yosymfony\Toml\Toml; +use Symfony\Component\Yaml\Yaml; class EntriesController { @@ -65,7 +65,7 @@ class EntriesController $entriesForDay = $utilities->scandir($dayPath); foreach ($entriesForDay as $entryForDay) { - $entry = Toml::parseFile($entryForDay); + $entry = Yaml::parseFile($entryForDay); if (isset($_GET['flat'])) { $entries[] = $entry; } else { diff --git a/src/Controllers/SubmissionController.php b/src/Controllers/SubmissionController.php index 2c3d603..8d7d9e6 100644 --- a/src/Controllers/SubmissionController.php +++ b/src/Controllers/SubmissionController.php @@ -3,13 +3,21 @@ namespace FlatFileForms\Controllers; use FlatFileForms\Builder; +use FlatFileForms\Form; +use FlatFileForms\HookManager; use FlatFileForms\Validator; -use Yosymfony\Toml\TomlBuilder; +use Symfony\Component\Yaml\Yaml; class SubmissionController { public function submit(Builder $builder, Validator $validator, string $formPath): array { + /**@var HookManager $hooks*/ + global $hooks; + + /**@var Form $form*/ + global $form; + $fields = $builder->buildFields(); // run through validation @@ -21,23 +29,19 @@ class SubmissionController if (empty($result['error'])) { $date = new \Datetime(); $entry = [ - 'fields' => $_POST, 'date' => $date->format('c'), + 'fields' => $_POST, ]; - $entryBuilder = new TomlBuilder(); - $entryBuilder->addValue('date', $entry['date']); - $entryBuilder->addTable('fields'); - foreach ($entry['fields'] as $entryKey => $entryValue) { - $entryBuilder->addValue($entryKey, $entryValue); - } + $entry = $hooks->applyFilter("submit:{$form->name}:entry", $entry); + $entry = $hooks->applyFilter('submit:entry', $entry); $entryDirectory = $formPath . '/entries/' . $date->format('Y/m/d'); @mkdir($entryDirectory, 0774, true); - $entryFilename = $date->format('Ymd_Hi_') . hash('adler32', serialize($entry)) . '.toml'; + $entryFilename = $date->format('Ymd_Hi_') . hash('adler32', serialize($entry)) . '.yaml'; file_put_contents( $entryDirectory . '/' . $entryFilename, - $entryBuilder->getTomlString() + Yaml::dump($entry, 4) ); } else { diff --git a/src/Form.php b/src/Form.php new file mode 100644 index 0000000..62527aa --- /dev/null +++ b/src/Form.php @@ -0,0 +1,22 @@ +<?php + +namespace FlatFileForms; + +class Form +{ + public string $name; + + public function __construct( + public string $path, + ) + { + $this->name = basename($path); + } + + public function getFields(mixed $page = null) + { + $builder = new Builder($this->path); + + return $builder->buildFields($page); + } +} diff --git a/src/HookManager.php b/src/HookManager.php index 0f252c8..215ff58 100644 --- a/src/HookManager.php +++ b/src/HookManager.php @@ -19,7 +19,7 @@ class HookManager public function doAction(string $name, mixed ...$arguments): void { - foreach ($this->actions[$name] as $actions) { + foreach ($this->actions[$name] ?? [] as $actions) { foreach ($actions as $action) { call_user_func_array($action['function'], $arguments); } @@ -31,7 +31,7 @@ class HookManager // set $value as first argument array_unshift($arguments, $value); - foreach ($this->filters[$name] as $filters) { + foreach ($this->filters[$name] ?? [] as $filters) { foreach ($filters as $filter) { $value = call_user_func_array($filter['function'], $arguments); } diff --git a/src/Validator.php b/src/Validator.php index 33cdf34..03d09b8 100644 --- a/src/Validator.php +++ b/src/Validator.php @@ -51,7 +51,7 @@ class Validator return $result; } - public function validateFields(array $fields): array + private function validateFields(array $fields): array { /**@var Utilities $utilities*/ global $utilities; @@ -71,8 +71,14 @@ class Validator return $fields; } - public function validateSingleField(array $field): array + private function validateSingleField(array $field): array { + /**@var HookManager $hooks*/ + global $hooks; + + /**@var Form $form*/ + global $form; + $value = $_POST[$field['name']] ?? ''; $field['is_valid'] = true; @@ -84,10 +90,10 @@ class Validator $field['is_valid'] = false; } - $validationFunctionName = 'validate_' . basename($this->formPath) . '_' . $field['name']; - if (function_exists($validationFunctionName)) { - $field = call_user_func($validationFunctionName, $field, $value); - } + $field = $hooks->applyFilter("validator:{$form->name}:field", $field, $value); + $field = $hooks->applyFilter("validator:{$form->name}:field:{$field['name']}", $field, $value); + $field = $hooks->applyFilter("validator:field:{$field['name']}", $field, $value); + $field = $hooks->applyFilter('validator:field', $field, $value); return $field; } diff --git a/tests/Test.php b/tests/Test.php index 2888d81..b111a82 100644 --- a/tests/Test.php +++ b/tests/Test.php @@ -19,64 +19,67 @@ class Test extends TestCase // add content $contentRoot = dirname(__DIR__) . '/content'; - @mkdir($contentRoot . '/config'); - @mkdir($contentRoot . '/customername/formname/config'); - @mkdir($contentRoot . '/customername/formname/fields'); - @mkdir($contentRoot . '/customername/pagedform/config'); - @mkdir($contentRoot . '/customername/pagedform/fields'); + @mkdir($contentRoot . '/config', recursive: true); + @mkdir($contentRoot . '/customername/formname/config', recursive: true); + @mkdir($contentRoot . '/customername/formname/fields', recursive: true); + @mkdir($contentRoot . '/customername/pagedform/config', recursive: true); + @mkdir($contentRoot . '/customername/pagedform/fields', recursive: true); # root config - file_put_contents($contentRoot . '/config/config.toml', <<<EOF - [app] - title = "Flat-File Forms" - - [email] - host = "localhost" - user = "root" - password = "123456" - - [api] - keys = ["1234"] - - [api.cors] - origins = ["http://localhost:8081"] + file_put_contents($contentRoot . '/config/config.yaml', <<<EOF + app: + title: Flat-File Forms + + email: + host: localhost + user: root + password: 123456 + + api: + keys: + - 1234 + cors: + origins: + - https://localhost:8081 EOF); # formname config - file_put_contents($contentRoot . '/customername/formname/config/config.toml', <<<EOF - [api] - keys = ["asdfghjklö0987654321", "123"] - - [email] - host = "gmx" + file_put_contents($contentRoot . '/customername/formname/config/config.yaml', <<<EOF + api: + keys: + - asdfghjklö0987654321 + - 123 + + email: + host: gmx.de EOF); # formname fields - file_put_contents($contentRoot . '/customername/formname/fields/_fields.toml', <<<EOF - [field.name] - file = "name.toml" + file_put_contents($contentRoot . '/customername/formname/fields/_fields.yaml', <<<EOF + name: + file: name.yaml - [field.email] - file = "email.toml" - required = true + email: + file: email.yaml + required: true - [field.date] - required = true + date: + required: true EOF); - file_put_contents($contentRoot . '/customername/formname/fields/email.toml', <<<EOF - type = "email" - name = "email" - placeholder = "E-Mail" + file_put_contents($contentRoot . '/customername/formname/fields/email.yaml', <<<EOF + type: email + name: email + placeholder: E-Mail - [attributes] - data-email = "test@example.org" + attributes: + data-email: test@example.org EOF); - file_put_contents($contentRoot . '/customername/formname/fields/name.toml', <<<EOF - type = "text" - name = "name" + file_put_contents($contentRoot . '/customername/formname/fields/name.yaml', <<<EOF + type: text + name: name - [attributes] - data-value = 123 + attributes: + data-value: 123 EOF); file_put_contents($contentRoot . '/customername/formname/config/functions.php', <<<EOF @@ -89,55 +92,62 @@ class Test extends TestCase return \$field; } + + global \$hooks; + \$hooks->addFilter('validator:formname:field:name', 'validate_formname_name'); EOF); # pagedform fields - file_put_contents($contentRoot . '/customername/pagedform/fields/_fields.toml', <<<EOF - [page.one.field.name] - file = "name.toml" - - [page.one.field.email] - file = "email.toml" - required = true - - [page.second] - file = "second.toml" - - [page.second.field.date] - required = true + file_put_contents($contentRoot . '/customername/pagedform/fields/_fields.yaml', <<<EOF + pages: + one: + fields: + name: + file: name.yaml + email: + file: email.yaml + required: true + + second: + file: second.yaml + fields: + date: + required: true EOF); - file_put_contents($contentRoot . '/customername/pagedform/fields/second.toml', <<<EOF - [field.text] - placeholder = "Text placeholder" - - [field.text.validation] - pattern = "\\\d+" - - [field.manythings] - type = "select" - - [field.manythings.options] - first = "First level" - second = "Second level" - - [field.multiplethings] - type = "checkbox" - options = ["thing-1", "thing-2", "thing-3"] + file_put_contents($contentRoot . '/customername/pagedform/fields/second.yaml', <<<EOF + fields: + text: + placeholder: Text placeholder + validation: + pattern: "\\\d+" + + manythings: + type: select + options: + first: First level + second: Second level + + multiplethings: + type: checkbox + options: + - thing-1 + - thing-2 + - thing-3 EOF); - file_put_contents($contentRoot . '/customername/pagedform/fields/email.toml', <<<EOF - type = "email" - name = "email" - placeholder = "E-Mail" + file_put_contents($contentRoot . '/customername/pagedform/fields/email.yaml', <<<EOF + type: email + name: email + placeholder: E-Mail - [attributes] - data-email = "test@example.org" + attributes: + data-email: test@example.org EOF); - file_put_contents($contentRoot . '/customername/pagedform/fields/name.toml', <<<EOF - type = "text" - name = "name" + file_put_contents($contentRoot . '/customername/pagedform/fields/name.yaml', <<<EOF + type: text + name: name - [attributes] - data-value = 123 + attributes: + data-value: 123 EOF); } |