From ee93907398cc34ffe94503b49e406668c95c8c45 Mon Sep 17 00:00:00 2001 From: Nastro Date: Thu, 24 Aug 2023 20:14:04 +0300 Subject: [PATCH] Update php version to 8.2 (#8) * update to php8.2, update phpunit, fix tests * fix * upd symfony * fixes DI, cache, smarty * small fixes * removed exceptions in json_decode and json_encode, changelog, change version * fix cache application, upd ApplicationInterface, delete final in Application, fix spaces * fix spaces --------- Co-authored-by: Yury Bushenko --- .gitignore | 1 + CHANGELOG.md | 22 + composer.json | 27 +- .../samples/old/assets/posteddata.php | 117 +- lib/Acl/acl.class.inc | 2 +- lib/Acl/object.class.inc | 4 +- lib/Common/functions.array.inc | 14 +- lib/Common/functions.compatibility.inc | 71 +- lib/Common/functions.console.inc | 94 +- lib/Common/functions.etc.inc | 10 +- lib/Common/functions.file.inc | 22 +- lib/Common/functions.html.inc | 10 +- lib/Common/functions.http.inc | 88 +- lib/Common/functions.imageswork.inc | 64 +- lib/Common/functions.path.inc | 12 +- lib/Common/functions.pp.inc | 6 +- lib/Common/functions.string.inc | 118 +- lib/Common/functions.test.inc | 38 +- lib/Common/functions.validate.inc | 12 +- .../Bindings/struct-match.class.inc | 5 +- lib/Config/Description/db.class.inc | 16 +- lib/Config/Description/directory.class.inc | 6 +- lib/Config/Description/field.class.inc | 10 +- lib/Config/Description/module.class.inc | 3 +- lib/Config/Description/ref.class.inc | 2 +- lib/Config/Description/trigger.class.inc | 11 +- lib/Config/Description/type.class.inc | 31 +- lib/Config/application.class.inc | 80 +- lib/Config/bindingsQueue.class.inc | 8 +- lib/Database/database.class.inc | 171 ++- lib/Debug/ErrorReporter/abstract.class.inc | 4 +- lib/Debug/ErrorReporter/html.class.inc | 49 +- lib/Debug/functions.inc | 2 +- lib/Debug/trace.inc | 39 +- lib/DisplayType/Dropdown/dropdown.class.inc | 6 +- .../Dropdown/multiDropdown.class.inc | 6 +- .../Dropdown/parentDropdown.class.inc | 7 +- .../Dropdown/selfParentDropdown.class.inc | 2 +- .../Dropdown/treeDropdown.class.inc | 2 +- lib/DisplayType/File/file.class.inc | 4 +- lib/DisplayType/File/filesArray.class.inc | 3 +- lib/DisplayType/File/image.class.inc | 6 +- lib/DisplayType/File/imagesArray.class.inc | 4 +- lib/DisplayType/Time/date.class.inc | 3 +- lib/DisplayType/Time/time.class.inc | 3 +- lib/DisplayType/abstract.class.inc | 16 +- lib/DisplayType/checkbox.class.inc | 3 +- lib/DisplayType/color.class.inc | 4 +- lib/DisplayType/ipAddr.class.inc | 4 +- lib/DisplayType/jobResult.class.inc | 6 +- lib/DisplayType/password.class.inc | 8 +- lib/DisplayType/radiolist.class.inc | 3 +- lib/DisplayType/richedit.class.inc | 2 +- lib/DisplayType/static.class.inc | 5 +- lib/DisplayType/staticordrop.class.inc | 4 +- lib/DisplayType/table.class.inc | 12 +- lib/DisplayType/tableStatic.class.inc | 3 +- lib/DisplayType/text.class.inc | 12 +- lib/Engine/json.class.inc | 2 +- lib/Engine/preview.class.inc | 5 +- lib/Engine/sbin.class.inc | 2 +- lib/Filesys/dir.class.inc | 13 +- lib/Filesys/listing.class.inc | 18 +- lib/HTML/Abstract/htmlform.class.inc | 1 + .../Decorators/widgetscollection.class.inc | 27 +- .../Admin/Widgets/Control/button.class.inc | 12 +- .../Admin/Widgets/Control/close.class.inc | 5 +- .../Admin/Widgets/Control/reset.class.inc | 5 +- .../Admin/Widgets/Control/submit.class.inc | 9 +- .../Admin/Widgets/FieldGroups/table.class.inc | 2 +- .../Widgets/Input/Datetime/abstract.class.inc | 6 +- .../Input/Datetime/datecalendar.class.inc | 2 +- .../Widgets/Input/Datetime/time.class.inc | 4 +- .../Admin/Widgets/Input/checkbox.class.inc | 4 +- lib/HTML/Admin/Widgets/Input/color.class.inc | 15 +- .../Admin/Widgets/Input/dropdown.class.inc | 11 +- lib/HTML/Admin/Widgets/Input/file.class.inc | 17 +- lib/HTML/Admin/Widgets/Input/hidden.class.inc | 11 +- lib/HTML/Admin/Widgets/Input/image.class.inc | 7 +- lib/HTML/Admin/Widgets/Input/input.class.inc | 30 +- lib/HTML/Admin/Widgets/Input/ip.class.inc | 7 +- lib/HTML/Admin/Widgets/Input/link.class.inc | 11 +- .../Widgets/Input/parentdropdown.class.inc | 14 +- .../Admin/Widgets/Input/password.class.inc | 8 +- .../Admin/Widgets/Input/radiobutton.class.inc | 2 +- .../Admin/Widgets/Input/radiolist.class.inc | 14 +- .../Admin/Widgets/Input/richedit.class.inc | 15 +- lib/HTML/Admin/Widgets/Input/table.class.inc | 16 +- .../Admin/Widgets/Input/textarea.class.inc | 17 +- .../Admin/Widgets/Objects/abstract.class.inc | 2 +- .../Admin/Widgets/Objects/ajaxtree.class.inc | 7 +- .../Widgets/Objects/blockstree.class.inc | 23 +- .../Admin/Widgets/Objects/table.class.inc | 8 +- lib/HTML/Admin/Widgets/Objects/tree.class.inc | 41 +- lib/HTML/Admin/Widgets/abstract.class.inc | 6 +- .../Admin/Widgets/ajaxTreeBranch.class.inc | 5 +- lib/HTML/Admin/Widgets/ajaxtree.class.inc | 4 +- lib/HTML/Admin/Widgets/form.class.inc | 63 +- lib/HTML/Admin/Widgets/list.class.inc | 4 +- lib/HTML/Admin/Widgets/menuTabbed.class.inc | 6 +- lib/HTML/Admin/Widgets/pager.class.inc | 28 +- lib/HTML/Admin/Widgets/table.class.inc | 35 +- lib/HTML/Admin/Widgets/tablesimple.class.inc | 12 +- lib/HTML/Admin/Widgets/tabs.class.inc | 1 + lib/HTML/Admin/Widgets/tree.class.inc | 5 +- lib/HTML/Admin/Widgets/treeBranch.class.inc | 8 +- lib/HTML/assets.class.inc | 18 +- lib/HTML/assetsbundler.class.inc | 16 +- lib/HTML/filelisting.class.inc | 20 +- lib/HTML/lang.class.inc | 18 +- lib/HTML/layout.class.inc | 30 +- lib/LazyLoader/blockcontent.class.inc | 18 +- lib/Logger/Audit/table.class.inc | 16 +- lib/Logger/Audit/wrapper.class.inc | 12 +- lib/Logger/nllogger.class.inc | 24 +- lib/Mail/EventCalendar/vevent.class.inc | 2 +- lib/Mail/message.class.inc | 45 +- lib/Objects/abstract.class.inc | 2 +- lib/Objects/objects.class.inc | 4 +- lib/Objects/struct.class.inc | 10 +- lib/Request/HttpVars/base.class.inc | 20 +- lib/Request/HttpVars/post.class.inc | 6 +- lib/Request/request.class.inc | 118 +- lib/Security/blockingnumbers.class.inc | 4 +- lib/Security/captcha.class.inc | 54 +- lib/StorageType/File/abstract.class.inc | 58 +- lib/StorageType/File/file.class.inc | 5 +- lib/StorageType/File/filesarray.class.inc | 5 +- lib/StorageType/File/image.class.inc | 7 +- lib/StorageType/File/imagesarray.class.inc | 3 +- lib/StorageType/abstract.class.inc | 14 +- lib/StorageType/array.class.inc | 6 +- lib/StorageType/flatintarray.class.inc | 4 +- lib/StorageType/intarray.class.inc | 8 +- lib/StorageType/json.class.inc | 2 +- lib/StorageType/serialized.class.inc | 8 +- lib/StorageType/sql.class.inc | 3 +- lib/StorageType/timestamp.class.inc | 3 +- lib/Text/typographer.php | 9 +- lib/Triggers/db.class.inc | 32 +- lib/Triggers/system.class.inc | 4 +- lib/User/abstract.class.inc | 23 +- lib/User/auth.class.inc | 2 +- lib/XML/XSLT/abstract.class.inc | 4 +- lib/XML/XSLT/libxslt.class.inc | 6 +- lib/common.defines.inc | 8 +- lib/common.version.inc | 2 +- lib/loader.class.inc | 10 +- lib/logger.class.inc | 17 +- lib/mainadmin.inc | 2 +- lib/maincommon.inc | 4 +- lib/mainuser.inc | 2 +- lib/registry.class.inc | 20 +- lib/search.class.inc | 28 +- lib/smarty.plugins/compiler.break.php | 2 +- lib/smarty.plugins/compiler.continue.php | 2 +- lib/smarty.plugins/modifier.date_to_time.php | 4 +- plugins/multipleregions/lib/cloner.class.inc | 2 +- .../lib/events_callback.class.inc | 2 +- .../multipleregions/sbin/cleanUpRegions.php | 2 +- .../multipleregions/sbin/renameReflexId.php | 2 +- src/ApplicationFactory.php | 119 +- src/Command/CompileContainerCommand.php | 68 +- src/Command/CronCommand.php | 168 +-- src/Command/FillMetaCommand.php | 124 +- src/Command/FillUuidCommand.php | 199 +-- src/Command/GetPropertyCommand.php | 82 +- src/Command/Migrate/MigrateCreateCommand.php | 80 +- src/Command/Migrate/MigrateListCommand.php | 50 +- src/Command/Migrate/MigrateUpCommand.php | 159 +-- src/Command/SetPropertyCommand.php | 82 +- src/ConfigurationLocator.php | 89 +- src/ConsoleApplication.php | 412 +++--- src/Cron/AbstractCron.php | 82 +- src/Cron/CronRule.php | 235 ++-- .../Compiler/AddLoggingHandlersPass.php | 28 +- src/DependencyInjection/Configuration.php | 21 +- src/DependencyInjection/CoreExtension.php | 81 +- src/Lib/ArrayCollection.php | 146 ++- src/Lib/Auth/AuthAbstract.php | 301 +++-- src/Lib/Auth/AuthException.php | 3 +- src/Lib/Auth/AuthInterface.php | 152 +-- src/Lib/Auth/NullAuth.php | 32 +- src/Lib/Auth/Session.php | 138 +- src/Lib/Cache/Driver/Apc.php | 8 +- src/Lib/Cache/Driver/File.php | 468 +++---- src/Lib/Cache/Driver/Memcached.php | 250 ++-- src/Lib/Cache/Driver/Predis.php | 292 +++-- src/Lib/Cache/Driver/Redis.php | 315 ++--- src/Lib/Cache/ObjectCache.php | 106 +- src/Lib/Collection.php | 255 ++-- src/Lib/Command/AbstractBasicCommand.php | 3 +- src/Lib/Command/AbstractCommand.php | 58 +- src/Lib/Command/MigrateAbstractCommand.php | 170 +-- src/Lib/Config/ApplicationInterface.php | 27 + .../InitializableDescriptionInterface.php | 17 +- src/Lib/Console/Output/BuferringOutput.php | 62 +- src/Lib/Console/Report/Mailer.php | 344 ++--- src/Lib/Database/AbstractSqlDatabase.php | 345 +++-- src/Lib/Database/DatabaseAdapter.php | 168 +-- src/Lib/Database/DatabaseException.php | 3 +- src/Lib/Database/DatabaseInterface.php | 8 +- src/Lib/Database/Driver/PostgreSqlDriver.php | 873 ++++++------ src/Lib/Datastruct/Leaf.php | 140 +- src/Lib/Datastruct/Tree.php | 754 +++++------ src/Lib/Engine/AbstractEngine.php | 377 +++--- src/Lib/Engine/Admin/AbstractAdminEngine.php | 93 +- src/Lib/Engine/Admin/AdminEngineIndex.php | 283 ++-- src/Lib/Engine/Admin/AdminEngineInterface.php | 4 +- src/Lib/Engine/Admin/AdminEngineJson.php | 83 +- src/Lib/Engine/Admin/AdminEnginePopup.php | 1 - src/Lib/Engine/EngineInterface.php | 112 +- src/Lib/Html/Layout/AdminHtmlLayout.php | 573 ++++---- src/Lib/Html/Layout/LayoutAbstract.php | 967 +++++++------- src/Lib/Html/Layout/LayoutInterface.php | 88 +- src/Lib/Html/Layout/NullLayout.php | 1 - src/Lib/Http/Response.php | 634 +++++---- src/Lib/IArrayable.php | 29 +- src/Lib/Objects/ContentObjectsInterface.php | 21 +- src/Lib/PersistentQueue/Job.php | 495 +++---- src/Lib/PersistentQueue/JobResult.php | 301 ++--- src/Lib/PersistentQueue/Queue.php | 210 +-- src/Lib/PersistentQueue/WorkerInterface.php | 22 +- src/Lib/Rss/AbstractRssNode.php | 43 +- src/Lib/Rss/RssChannel.php | 87 +- src/Lib/Rss/RssItem.php | 1 - src/Lib/Session/DatabaseHandler.php | 260 ++-- src/Lib/UrlGenerator/AbstractUrlGenerator.php | 141 +- src/Lib/UrlGenerator/AdminUrlGenerator.php | 100 +- src/Lib/UrlGenerator/ContextUrlGenerator.php | 206 +-- src/Lib/UrlGenerator/GeneratorInterface.php | 62 +- src/Lib/UrlGenerator/UrlGenerator.php | 106 +- src/Lib/UrlGenerator/UserUrlGenerator.php | 60 +- src/Lib/Xml/AbstractXml.php | 71 +- src/Lib/Xml/AbstractXmlNode.php | 310 ++--- src/Lib/Xml/SimpleXml.php | 60 +- src/Lib/Xml/SimpleXmlNode.php | 192 ++- src/Lib/Xml/Xml.php | 101 +- src/Lib/Xml/XmlErrors.php | 64 +- src/Lib/Xml/XmlInterface.php | 11 +- src/Lib/Xml/XmlNodeInterface.php | 95 +- src/Migration/MigrationAbstract.php | 67 +- src/Module/AbstractModule.php | 213 ++- src/Module/AclModule.php | 1166 ++++++++--------- src/Module/AuditLogModule.php | 27 +- src/Module/AuthModule.php | 19 +- src/Module/CronRunModule.php | 611 +++++---- src/Module/CsvImportModule.php | 533 ++++---- src/Module/FileModule.php | 870 ++++++------ src/Module/MaclModule.php | 119 +- src/Module/MainModule.php | 699 +++++----- src/Module/MassChangeModule.php | 273 ++-- src/Module/ModuleInterface.php | 82 +- src/Module/ObjectsModule.php | 1124 ++++++++-------- src/Module/PropertiesModule.php | 1080 +++++++-------- src/Module/RssEngineModule.php | 9 +- src/Module/SearchModule.php | 105 +- src/Plugin/AbstractPlugin.php | 190 ++- src/Plugin/SortableAcl/Sortable.php | 102 +- src/Properties/EnvLoader.php | 303 +++-- src/Properties/EnvLoaderException.php | 3 +- src/Properties/PropertyLoader.php | 102 +- src/Serializer/DefaultSerializer.php | 60 +- src/Serializer/IgbinarySerializer.php | 60 +- src/Serializer/SerializerAwareInterface.php | 9 +- src/Serializer/SerializerAwareTrait.php | 22 +- src/Serializer/SerializerFactory.php | 39 +- src/Serializer/SerializerInterface.php | 40 +- tests/Base/AbstractApplicationTest.php | 7 +- tests/Unit/PP/CollectionTest.php | 4 +- tests/Unit/PP/Lib/PersistentQueue/JobTest.php | 35 +- .../Unit/PP/Lib/PersistentQueue/QueueTest.php | 45 +- .../UrlGenerator/AdminUrlGeneratorTest.php | 123 +- .../PP/Lib/UrlGenerator/UrlGeneratorTest.php | 21 +- .../Lib/UrlGenerator/UserUrlGeneratorTest.php | 105 +- tests/Unit/PP/Properties/EnvLoaderTest.php | 7 +- vendor/Smarty/Smarty_Compiler.class.php | 2 +- 277 files changed, 12164 insertions(+), 12512 deletions(-) create mode 100644 src/Lib/Config/ApplicationInterface.php diff --git a/.gitignore b/.gitignore index 31fc5ca8..67fbcee1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ composer.lock *.iml .idea +*.cache diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ced8b17..c8ebffb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Change log +## [3.0.0] 2023-08-23 +- Обновлена версия php до 8.2 +- Обновление phpunit/phpunit до 9.0 lock = `9.6.11` +- Обновление friendsofphp/php-cs-fixer до 3.23 lock = `3.23.0` +- Обновлены мажорные версии зависимостей symfony: + - `symfony/http-foundation` `^6.0` lock = `6.3.2` + https://github.com/symfony/http-foundation/blob/master/CHANGELOG.md + - `symfony/console` `^6.0` lock = `6.3.2` + https://github.com/symfony/console/blob/master/CHANGELOG.md + - `symfony/event-dispatcher` `^6.0` lock = `6.3.2` + https://github.com/symfony/event-dispatcher/blob/master/CHANGELOG.md + - `symfony/yaml` `^6.0` lock = `6.3.3` + https://github.com/symfony/var-dumper/blob/master/CHANGELOG.md + - `symfony/config` `^6.0` lock = `6.3.2` + https://github.com/symfony/config/blob/master/CHANGELOG.md + - `symfony/dependency-injection` `^6.0` lock = `6.3.2` + https://github.com/symfony/dependency-injection/blob/master/CHANGELOG.md + - `symfony/finder` `^6.0` lock = `6.3.3` + https://github.com/symfony/finder/blob/6.3/CHANGELOG.md + - `symfony/cache` `^6.0` lock = `6.3.2` + https://github.com/symfony/cache/blob/6.3/CHANGELOG.md + ## [2.4.11] 2023-03-20 - Исправлено поведение плагина `PXPluginFilters` для полей линейки `PXDisplayTypeDropdown`, у которых отсутствуют загруженные значения для построения выпадающего списка. Для таких полей теперь используется обычный input. diff --git a/composer.json b/composer.json index 1fc0a300..242dd055 100644 --- a/composer.json +++ b/composer.json @@ -11,26 +11,29 @@ ], "config": { "platform": { - "php": "7.2" + "php": "8.2" + }, + "allow-plugins": { + "dalee/pp-installers": true } }, "bin": [ "bin/pp" ], "require": { - "php": ">=7.2", + "php": "^8.2", "dalee/pp-installers": "~1.0.5", "monolog/monolog": "^1.19", - "symfony/http-foundation": "~3.4", + "symfony/http-foundation": "^6.0", "ramsey/uuid": "^3.5", - "symfony/console": "~3.4", - "symfony/event-dispatcher": "~3.4", - "symfony/yaml": "~3.4", + "symfony/console": "^6.0", + "symfony/event-dispatcher": "^6.0", + "symfony/yaml": "^6.0", "vlucas/phpdotenv": "^2.4", - "symfony/config": "~3.4", - "symfony/dependency-injection": "~3.4", - "symfony/finder": "~3.4", - "symfony/cache": "~3.4", + "symfony/config": "^6.0", + "symfony/dependency-injection": "^6.0", + "symfony/finder": "^6.0", + "symfony/cache": "^6.0", "predis/predis": "^1.1", "ext-json": "*", "ext-pcntl": "*", @@ -44,8 +47,8 @@ "ext-mbstring": "*" }, "require-dev": { - "phpunit/phpunit": "^4.8", - "friendsofphp/php-cs-fixer": "^2.7" + "phpunit/phpunit": "^9.0", + "friendsofphp/php-cs-fixer": "^3.23" }, "autoload": { "psr-4": { diff --git a/htdocs/admin/tools/vendor/ckeditor/samples/old/assets/posteddata.php b/htdocs/admin/tools/vendor/ckeditor/samples/old/assets/posteddata.php index ae156868..1f1b7261 100644 --- a/htdocs/admin/tools/vendor/ckeditor/samples/old/assets/posteddata.php +++ b/htdocs/admin/tools/vendor/ckeditor/samples/old/assets/posteddata.php @@ -1,60 +1,57 @@ - - - - - - Sample — CKEditor - - - - -

- CKEditor — Posted Data -

- - - - - - - - - $value ) - { - if ( ( !is_string($value) && !is_numeric($value) ) || !is_string($key) ) - continue; - - if ( get_magic_quotes_gpc() ) - $value = htmlspecialchars( stripslashes((string)$value) ); - else - $value = htmlspecialchars( (string)$value ); -?> - - - - - -
Field NameValue
- - - + + + + + + Sample — CKEditor + + + + +

+ CKEditor — Posted Data +

+ + + + + + + + + $value ) + { + if ( ( !is_string($value) && !is_numeric($value) ) || !is_string($key) ) + continue; + + $value = htmlspecialchars( (string)$value ); +?> + + + + + +
Field NameValue
+ + + diff --git a/lib/Acl/acl.class.inc b/lib/Acl/acl.class.inc index 8728a2b2..322be88e 100644 --- a/lib/Acl/acl.class.inc +++ b/lib/Acl/acl.class.inc @@ -66,7 +66,7 @@ class PXObjectsACL if (!$loadAll) { $query .= " WHERE (sgroupid IS NULL"; - if ($this->user && count($this->user->groups)) { + if ($this->user && (is_countable($this->user->groups) ? count($this->user->groups) : 0)) { $query .= ' or sgroupid IN (' . implode(',', $this->user->groups) . ')'; } diff --git a/lib/Acl/object.class.inc b/lib/Acl/object.class.inc index 480297ed..b935033c 100644 --- a/lib/Acl/object.class.inc +++ b/lib/Acl/object.class.inc @@ -59,7 +59,7 @@ class ObjectChecker { if ((bool)$parentFormat->parent) { $this->_loadParent($format, $object, $pKey); - $object['parent'] = isset($this->acl->parents[$pKey]) ? $this->acl->parents[$pKey] : null; + $object['parent'] = $this->acl->parents[$pKey] ?? null; } return $this->_checkGroup($what, $parentFormat, $object); @@ -73,7 +73,7 @@ class ObjectChecker { $tmpParents = $this->acl->db->query('SELECT parent FROM ' . $format->parent . ' WHERE id = ' . $object['parent']); - if (count($tmpParents)) { + if (is_countable($tmpParents) ? count($tmpParents) : 0) { $this->acl->parents[$pKey] = current(current($tmpParents)); } } diff --git a/lib/Common/functions.array.inc b/lib/Common/functions.array.inc index 670db083..c53ce4b5 100644 --- a/lib/Common/functions.array.inc +++ b/lib/Common/functions.array.inc @@ -156,7 +156,7 @@ function array_transpose($array) { * @param bool $strict * @return array resulting array with selected keys */ -function array_remap($array, $map, $strict = false) { +function array_remap($array, mixed $map, $strict = false) { if (!is_string($map) && !is_array($map)) { return false; } @@ -176,7 +176,7 @@ function array_remap($array, $map, $strict = false) { } foreach ($map as $k => $v) { - $result[$k] = isset($array[$v]) ? $array[$v] : null; + $result[$k] = $array[$v] ?? null; } return $result; @@ -186,7 +186,7 @@ function array_deep_diff($d1, $d2, $excludeKeys) { switch (true) { case is_array($d1) && is_array($d2) : $diff = []; - foreach (array_unique(array_merge(array_keys($d1), array_keys($d2))) as $key) { + foreach (array_unique([...array_keys($d1), ...array_keys($d2)]) as $key) { if (is_array($excludeKeys) && in_array($key, $excludeKeys)) { continue; } @@ -243,7 +243,7 @@ function array_deep_diff($d1, $d2, $excludeKeys) { * array_flat($arr, 'c', true); // array( 'oof' => 'x', 'zzz' => 'y', '123' => 'z', ... ) * array_flat($arr, false, array('a', 'c')); // array( array('a' => 'foo', 'c' => 'oof'), array('a' => 'bar', 'c' => 'zzz'), ... ) */ -function array_flat($array, $toKey = false, $toValue = false, $unique = false) { +function array_flat($array, $toKey = false, mixed $toValue = false, $unique = false) { if (!is_array($array)) { return false; @@ -327,7 +327,7 @@ function array_keys_grouped($array) { * @param bool $strict Использовать строгое (true) или нестрогое (false) сравнение при поиске элемента. * @return array Список из двух элементов - предыдущего и последующего. */ -function findNeighborElements($list, $current, $circular = true, $strict = true) { +function findNeighborElements($list, mixed $current, $circular = true, $strict = true) { $inList = &$list; // Иначе foreach не меняет указатель массива по завершению, что ломает весь алгоритм @@ -371,7 +371,7 @@ function findNeighborElements($list, $current, $circular = true, $strict = true) * * @return array Таблица из строк, для которых в заданном столбце ячейка имеет заданное значение. Если ничего не найдено, то пустой массив. */ -function getRowsFromTable($array, $colName, $colValues, $strict = true, $count = INF) { +function getRowsFromTable($array, $colName, mixed $colValues, $strict = true, $count = INF) { $found = []; $nFound = 0; $colValues = (array)$colValues; @@ -398,7 +398,7 @@ function getRowsFromTable($array, $colName, $colValues, $strict = true, $count = * * @return array Строка, для которой в заданном столбце ячейка имеет заданное значение, либо пустой массив в случае, если ничего не найдено. */ -function getRowFromTable($array, $colName, $colValues, $strict = true) { +function getRowFromTable($array, $colName, mixed $colValues, $strict = true) { $found = getRowsFromTable($array, $colName, $colValues, $strict, 1); count($found) && $found = reset($found); return $found; diff --git a/lib/Common/functions.compatibility.inc b/lib/Common/functions.compatibility.inc index 6bffe01d..4711ce5f 100644 --- a/lib/Common/functions.compatibility.inc +++ b/lib/Common/functions.compatibility.inc @@ -17,7 +17,7 @@ if (! function_exists('mb_strcasecmp')) { $encoding = mb_internal_encoding(); } - return strcmp(mb_strtoupper($str1, $encoding), mb_strtoupper($str2, $encoding)); + return strcmp(mb_strtoupper((string) $str1, $encoding), mb_strtoupper((string) $str2, $encoding)); } } @@ -33,79 +33,22 @@ if (! function_exists('mb_strtr')) { if (! function_exists('mb_str_pad')) { function mb_str_pad($input, $pad_length, $pad_string=' ', $pad_type=STR_PAD_RIGHT) { - $diff = strlen($input) - mb_strlen($input); - return str_pad($input, $pad_length+$diff, $pad_string, $pad_type); + $diff = strlen((string) $input) - mb_strlen((string) $input); + return str_pad((string) $input, $pad_length+$diff, $pad_string, $pad_type); } } if (! function_exists('mb_ucfirst')) { function mb_ucfirst($string) { - $l = mb_substr($string, 0, 1); + $l = mb_substr((string) $string, 0, 1); $l = mb_strtoupper($l); - return $l.mb_substr($string, 1); + return $l.mb_substr((string) $string, 1); } } if (! function_exists('mb_lcfirst')) { function mb_lcfirst($string) { - $l = mb_substr($string, 0, 1); + $l = mb_substr((string) $string, 0, 1); $l = mb_strtolower($l); - return $l.mb_substr($string, 1); - } -} - -// mbstring compatibility layer -if (! defined('MB_OVERLOAD_STRING')) { - define('MB_DISABLED', true); - - function mb_substr() { - $a = func_get_args(); - return call_user_func_array('substr', $a); - } - - function mb_strpos() { - $a = func_get_args(); - return call_user_func_array('strpos', $a); - } - - function mb_stripos() { - $a = func_get_args(); - return call_user_func_array('stripos', $a); - } - - function mb_strrpos() { - $a = func_get_args(); - return call_user_func_array('strrpos', $a); - } - - function mb_strlen() { - $a = func_get_args(); - $a = array_slice($a, 0, 1); - return call_user_func_array('strlen', $a); - } - - function mb_strtoupper() { - $a = func_get_args(); - $a = array_slice($a, 0, 1); - return call_user_func_array('strtoupper', $a); - } - - function mb_strtolower() { - $a = func_get_args(); - $a = array_slice($a, 0, 1); - return call_user_func_array('strtolower', $a); - } - - function mb_strstr() { - $a = func_get_args(); - return call_user_func_array('strstr', $a); - } - - function mb_stristr() { - $a = func_get_args(); - return call_user_func_array('stristr', $a); - } - - function mb_parse_str($str, &$arr = null) { - return parse_str($str, $arr); + return $l.mb_substr((string) $string, 1); } } diff --git a/lib/Common/functions.console.inc b/lib/Common/functions.console.inc index 994c4340..45895a13 100644 --- a/lib/Common/functions.console.inc +++ b/lib/Common/functions.console.inc @@ -5,35 +5,35 @@ * @version 0.1 */ -define('CON_COLOR_BLACK', "\033[00;30m", 1); -define('CON_COLOR_DGRAY', "\033[01;30m", 1); -define('CON_COLOR_RED', "\033[00;31m", 1); -define('CON_COLOR_LRED', "\033[01;31m", 1); -define('CON_COLOR_GREEN', "\033[00;32m", 1); -define('CON_COLOR_LGREEN', "\033[01;32m", 1); -define('CON_COLOR_BROWN', "\033[00;33m", 1); -define('CON_COLOR_YELLOW', "\033[01;33m", 1); -define('CON_COLOR_BLUE', "\033[00;34m", 1); -define('CON_COLOR_LBLUE', "\033[01;34m", 1); -define('CON_COLOR_PURPLE', "\033[00;35m", 1); -define('CON_COLOR_LPURPLE', "\033[01;35m", 1); -define('CON_COLOR_CYAN', "\033[00;36m", 1); -define('CON_COLOR_LCYAN', "\033[01;36m", 1); -define('CON_COLOR_LGRAY', "\033[00;37m", 1); -define('CON_COLOR_WHITE', "\033[01;37m", 1); - -define('CON_BGCOLOR_BLACK', "\033[40m", 1); -define('CON_BGCOLOR_RED', "\033[41m", 1); -define('CON_BGCOLOR_GREEN', "\033[42m", 1); -define('CON_BGCOLOR_YELLOW', "\033[43m", 1); -define('CON_BGCOLOR_BLUE', "\033[44m", 1); -define('CON_BGCOLOR_MAGENTA', "\033[45m", 1); -define('CON_BGCOLOR_CYAN', "\033[46m", 1); -define('CON_BGCOLOR_WHITE', "\033[47m", 1); +define('CON_COLOR_BLACK', "\033[00;30m"); +define('CON_COLOR_DGRAY', "\033[01;30m"); +define('CON_COLOR_RED', "\033[00;31m"); +define('CON_COLOR_LRED', "\033[01;31m"); +define('CON_COLOR_GREEN', "\033[00;32m"); +define('CON_COLOR_LGREEN', "\033[01;32m"); +define('CON_COLOR_BROWN', "\033[00;33m"); +define('CON_COLOR_YELLOW', "\033[01;33m"); +define('CON_COLOR_BLUE', "\033[00;34m"); +define('CON_COLOR_LBLUE', "\033[01;34m"); +define('CON_COLOR_PURPLE', "\033[00;35m"); +define('CON_COLOR_LPURPLE', "\033[01;35m"); +define('CON_COLOR_CYAN', "\033[00;36m"); +define('CON_COLOR_LCYAN', "\033[01;36m"); +define('CON_COLOR_LGRAY', "\033[00;37m"); +define('CON_COLOR_WHITE', "\033[01;37m"); + +define('CON_BGCOLOR_BLACK', "\033[40m"); +define('CON_BGCOLOR_RED', "\033[41m"); +define('CON_BGCOLOR_GREEN', "\033[42m"); +define('CON_BGCOLOR_YELLOW', "\033[43m"); +define('CON_BGCOLOR_BLUE', "\033[44m"); +define('CON_BGCOLOR_MAGENTA', "\033[45m"); +define('CON_BGCOLOR_CYAN', "\033[46m"); +define('CON_BGCOLOR_WHITE', "\033[47m"); define('CON_COLS', (`tput cols` - 10)); -define('CON_EOC', "\033[00m", 1); +define('CON_EOC', "\033[00m"); define('CON_EOL', CON_EOC . PHP_EOL); function con_colors_array() { @@ -84,7 +84,7 @@ function con_colors_debug() { } function con_message($message, $color = null, $wide = false) { - $message = trim($message); + $message = trim((string) $message); // calc string length without colors $l = mb_strlen(preg_replace("@\033\\[(0[01];)?[0-9]{2}m|\\{([fb]g:[a-z]+|end)\\}@u", '', $message)); $r = ''; @@ -123,7 +123,7 @@ function con_printf($message) { } function _con_dump($args) { - if (count($args) < 2 || mb_strpos($args[0], '%') !== false) { + if ((is_countable($args) ? count($args) : 0) < 2 || mb_strpos((string) $args[0], '%') !== false) { return; } con_printf('{fg:dgray}<<<"dump"{end}{nl}'); @@ -135,7 +135,7 @@ function _con_dump($args) { * @param string $message * @param mixed $data,... */ -function con_debug($message, $data = null) { +function con_debug($message, mixed $data = null) { $message = con_vsprintf($message, array_slice(func_get_args(), 1)); echo con_message($message, CON_COLOR_LBLUE . CON_BGCOLOR_BLUE) . PHP_EOL; _con_dump(func_get_args()); @@ -145,7 +145,7 @@ function con_debug($message, $data = null) { * @param string $message * @param mixed $data,... */ -function con_info($message, $data = null) { +function con_info($message, mixed $data = null) { $message = con_vsprintf($message, array_slice(func_get_args(), 1)); echo con_message($message, CON_COLOR_WHITE . CON_BGCOLOR_BLUE, 1) . PHP_EOL; func_num_args() > 1 && array_map('var_dump', array_slice(func_get_args(), 1)); @@ -155,7 +155,7 @@ function con_info($message, $data = null) { * @param string $message * @param mixed $data,... */ -function con_warn($message, $data = null) { +function con_warn($message, mixed $data = null) { $message = con_vsprintf($message, array_slice(func_get_args(), 1)); echo con_message($message, CON_COLOR_WHITE . CON_BGCOLOR_RED) . PHP_EOL; func_num_args() > 1 && array_map('var_dump', array_slice(func_get_args(), 1)); @@ -165,7 +165,7 @@ function con_warn($message, $data = null) { * @param string $message * @param mixed $data,... */ -function con_alert($message, $data = null) { +function con_alert($message, mixed $data = null) { $message = con_vsprintf($message, array_slice(func_get_args(), 1)); echo con_message($message, CON_COLOR_WHITE . CON_BGCOLOR_RED, 1) . PHP_EOL; func_num_args() > 1 && array_map('var_dump', array_slice(func_get_args(), 1)); @@ -204,14 +204,14 @@ function con_list($list, $level = 0, $key_color = null, $value_color = null, $de $max_key_length = $max_item_length = 6; foreach ($list as $key => $item) { - $max_key_length = max($max_key_length, mb_strlen($key)); - !is_array($item) && $max_item_length = max($max_item_length, mb_strlen($item)); + $max_key_length = max($max_key_length, mb_strlen((string) $key)); + !is_array($item) && $max_item_length = max($max_item_length, mb_strlen((string) $item)); } foreach ($list as $key => $item) { $str = str_repeat(' ', $level*2); $str .= $delim_color . '- '; $str .= $key_color . $key; - $str .= $delim_color . ': ' . str_repeat(' ', $max_key_length - mb_strlen($key)); + $str .= $delim_color . ': ' . str_repeat(' ', $max_key_length - mb_strlen((string) $key)); if (is_array($item)) { echo $str . CON_EOL; @@ -232,9 +232,7 @@ function con_parse_args_string($s) { $result = $m['param']; // remove quotes in each param - $result = array_map(function($s) { - return preg_replace('@\'([^\']*)\'|"([^"]*)"@', '\1\2', $s); - }, $result); + $result = array_map(fn($s) => preg_replace('@\'([^\']*)\'|"([^"]*)"@', '\1\2', (string) $s), $result); // done return $result; @@ -243,11 +241,9 @@ function con_parse_args_string($s) { /** * args parser * - * @param string|array $argv - * @param array $aliases * @return array */ -function con_parse_args($argv = null, array $aliases = []) { +function con_parse_args(string|array $argv = null, array $aliases = []) { // normalize params $aliases = $aliases ?: []; @@ -264,7 +260,7 @@ function con_parse_args($argv = null, array $aliases = []) { if (!empty($argv) && ($script = BASEPATH.$argv[0]) && file_exists($script) && is_executable($script) - && mb_strpos(realpath($script), BASEPATH) === 0) { + && mb_strpos(realpath($script), (string) BASEPATH) === 0) { array_shift($argv); } @@ -324,13 +320,13 @@ function con_parse_args($argv = null, array $aliases = []) { array_unshift($argv, $arg); break; // oneletter option with value - case $isshortkey && mb_strlen($arg) > 2 && $arg[2] == '=': - [$k, $v] = explode('=', mb_substr($arg, 1), 2); + case $isshortkey && mb_strlen((string) $arg) > 2 && $arg[2] == '=': + [$k, $v] = explode('=', mb_substr((string) $arg, 1), 2); $setoption($k, $v); break; // flags pack in one arg: -abcdef - case $isshortkey && mb_strlen($arg) > 2: - for ($i=1, $l=mb_strlen($arg); $i<$l; $i++) { + case $isshortkey && mb_strlen((string) $arg) > 2: + for ($i=1, $l=mb_strlen((string) $arg); $i<$l; $i++) { $setflag($arg[$i]); } break; @@ -339,13 +335,13 @@ function con_parse_args($argv = null, array $aliases = []) { $key = $arg[1]; break; // long option with value - case $islongkey && mb_strlen($arg) > 3 && mb_strpos($arg, '='): - [$k, $v] = explode('=', mb_substr($arg, 2), 2); + case $islongkey && mb_strlen((string) $arg) > 3 && mb_strpos((string) $arg, '='): + [$k, $v] = explode('=', mb_substr((string) $arg, 2), 2); $setoption($k, $v); break; // long param key case $islongkey: - $key = mb_substr($arg, 2); + $key = mb_substr((string) $arg, 2); break; } } diff --git a/lib/Common/functions.etc.inc b/lib/Common/functions.etc.inc index 3b46e1c2..9dc02dfa 100644 --- a/lib/Common/functions.etc.inc +++ b/lib/Common/functions.etc.inc @@ -14,9 +14,9 @@ function createSomePathByParentId($tree, $id, $fieldname='pathname', $delim='/', if (isset($tree->leafs[$id])) { if ($inContent) { - $path = isset($tree->leafs[$id]->content[$fieldname]) ? $tree->leafs[$id]->content[$fieldname] : ''; + $path = $tree->leafs[$id]->content[$fieldname] ?? ''; } else { - $path = isset($tree->leafs[$id]->{$fieldname}) ? $tree->leafs[$id]->{$fieldname} : ''; + $path = $tree->leafs[$id]->{$fieldname} ?? ''; } } else { $path = ''; @@ -76,11 +76,11 @@ function parseBool ($s) { function __int32($numeric_value){ $x = (float)$numeric_value; - $y = pow(2,32); + $y = 2 ** 32; if ($x > $y) { - $x = pow(2,31)-1; - } elseif ($x >= pow(2,31)) { + $x = 2 ** 31-1; + } elseif ($x >= 2 ** 31) { $x -= $y; } diff --git a/lib/Common/functions.file.inc b/lib/Common/functions.file.inc index 1703d428..d3d75eae 100644 --- a/lib/Common/functions.file.inc +++ b/lib/Common/functions.file.inc @@ -184,14 +184,14 @@ function ReadFileToString($fileName) { function ReadFileToUString($fileName) { if (!file_exists($fileName)) FatalError(); - return utf8_encode(ReadFileToString($fileName)); + return mb_convert_encoding((string) ReadFileToString($fileName), 'UTF-8', 'ISO-8859-1'); } function WriteStringToFile($fileName, $string) { $fd = fopen($fileName, "w"); if ($fd) { if (flock($fd, LOCK_EX) === true) { - fwrite($fd, $string); + fwrite($fd, (string) $string); flock($fd, LOCK_UN); } else { FatalError("Ошибка при записи в файл ".$fileName." - невозможно заблокировать файл."); @@ -211,7 +211,7 @@ function AppendStringToFile($fileName, $string) { $fd = fopen($fileName, "a"); if ($fd) { if (flock($fd, LOCK_EX) === true) { - fwrite($fd, $string); + fwrite($fd, (string) $string); flock($fd, LOCK_UN); } else { FatalError("Ошибка при записи в файл ".$fileName." - невозможно заблокировать файл."); @@ -225,7 +225,7 @@ function AppendStringToFile($fileName, $string) { if (!function_exists("mime_content_type")) { function mime_content_type($file, $type = 'mime') { //without Fileinfo extension constants FILEINFO_* are undefined if(function_exists("finfo_file") && file_exists('/etc/magic')) { - $tmp = finfo_file(finfo_open(constant('FILEINFO_' . mb_strtoupper($type)), '/etc/magic'), $file); + $tmp = finfo_file(finfo_open(constant('FILEINFO_' . mb_strtoupper((string) $type)), '/etc/magic'), $file); } else { $tmp = exec(sprintf("file -bi '%s'", escapeshellcmd($file))); } @@ -234,7 +234,7 @@ if (!function_exists("mime_content_type")) { } function MakeDirIfNotExists($dirName, $perm = NULL) { - $dirName = preg_replace('|[/\\\\]+|'.REGEX_MOD, DIRECTORY_SEPARATOR, $dirName); + $dirName = preg_replace('|[/\\\\]+|'.REGEX_MOD, DIRECTORY_SEPARATOR, (string) $dirName); $fullDirName = ''; $dirPath = explode(DIRECTORY_SEPARATOR, $dirName); @@ -258,7 +258,7 @@ function MakeDirIfNotExists($dirName, $perm = NULL) { } function parse_csv_string($source, $delim=';', $withColumnNames=false) { - $strings = explode("\n", trim($source)); + $strings = explode("\n", trim((string) $source)); $res = []; @@ -272,11 +272,11 @@ function parse_csv_string($source, $delim=';', $withColumnNames=false) { $elements = explode($delim, $string); for ($i = 0; $i < count($elements); $i++) { - $nquotes = substr_count($elements[$i], '"'); + $nquotes = substr_count((string) $elements[$i], '"'); if ($nquotes %2 == 1) { for ($j = $i+1; $j < count($elements); $j++) { - if (substr_count($elements[$j], '"') > 0) { + if (substr_count((string) $elements[$j], '"') > 0) { // Put the quoted string's pieces back together again array_splice($elements, $i, $j-$i+1, implode($delim, array_slice($elements, $i, $j-$i+1))); @@ -288,7 +288,7 @@ function parse_csv_string($source, $delim=';', $withColumnNames=false) { if ($nquotes > 0) { // Remove first and last quotes, then merge pairs of quotes $qstr =& $elements[$i]; - $qstr = substr_replace($qstr, '', mb_strpos($qstr, '"'), 1); + $qstr = substr_replace((string) $qstr, '', mb_strpos((string) $qstr, '"'), 1); $qstr = substr_replace($qstr, '', mb_strrpos($qstr, '"'), 1); $qstr = str_replace('""', '"', $qstr); } @@ -335,7 +335,7 @@ function parse_csv_file($filename, $delim=';') { } function FindSystemFile($fileName){ - $pathArray = explode(':', $_SERVER['PATH']); + $pathArray = explode(':', (string) $_SERVER['PATH']); if (!in_array('/usr/X11R6/bin/', $pathArray) && !in_array('/usr/X11R6/bin', $pathArray)) { $pathArray[] = '/usr/X11R6/bin/'; } @@ -377,7 +377,7 @@ function fileMimeType ($filename) { * @test [1555, 2] >>> {"size":1.52,"step":1,"measure":"KB","pretty":"1.52 KB"} */ function parseFilesize ($filesize, $decimals = 0, $measures = null) { - $measures = (array)($measures ? $measures : null) + ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; + $measures = (array)($measures ?: null) + ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; $size = $filesize; $step = 0; diff --git a/lib/Common/functions.html.inc b/lib/Common/functions.html.inc index e264a49f..629c4290 100644 --- a/lib/Common/functions.html.inc +++ b/lib/Common/functions.html.inc @@ -34,7 +34,7 @@ function html_doctype ($type = 'html5') { $type = $aliases[$type]; } - $doctype = isset($doctypes[$type])? $doctypes[$type] : $doctypes['html5']; + $doctype = $doctypes[$type] ?? $doctypes['html5']; return ""; } @@ -110,7 +110,7 @@ function html_element ($el, $html, $atts = []) { } $text = isset($atts['text'])? html_escape($atts['text']) : ''; - $html = isset($atts['html'])? $atts['html'] : ''; + $html = $atts['html'] ?? ''; $html = is_array($html)? join($html) : $html; $inner = trim($text . $html); @@ -179,7 +179,7 @@ function _html_duck_params ($args, $names, $empties = false) { // collect return foreach ($args as $k => $v) { - $atts[$k] = !empty($v)? $v : (isset($atts[$k])? $atts[$k] : null); + $atts[$k] = !empty($v)? $v : ($atts[$k] ?? null); } return $empties? $atts : array_filter($atts); @@ -249,7 +249,7 @@ function html_block ($name, $atts = [], $html = '') { } // fetch tag if exists or use div - $tag = isset($atts['tag'])? $atts['tag'] : 'div'; + $tag = $atts['tag'] ?? 'div'; unset($atts['tag']); // prepend block class @@ -323,7 +323,7 @@ function html_img ($src, $class = null, $alt = '', array $atts = []) { * @test ["a,b"] >>> "b" --- try2 * @test ["a,b"] >>> "b" --- try3 */ -function html_cycle ($cycle, $name = null, $iterate = 1) { +function html_cycle (string|array $cycle, $name = null, $iterate = 1) { if (is_numeric($name) || is_bool($name)) { $iterate = $name; $name = null; diff --git a/lib/Common/functions.http.inc b/lib/Common/functions.http.inc index 51159b78..17ee4e6a 100644 --- a/lib/Common/functions.http.inc +++ b/lib/Common/functions.http.inc @@ -23,7 +23,7 @@ function http_parse_params_split ($string, $delim, $quote = '"') { $token = $delim; $part = strtok($string, $token); do { - if (strpos($part, $quote) !== false && strrpos($part, $quote) === strpos($part, $quote)) { + if (str_contains($part, (string) $quote) && strrpos($part, (string) $quote) === strpos($part, (string) $quote)) { $prevpart = $part.$token; $part = strtok($quote) . $quote; $r[] = $prevpart . $part; @@ -42,8 +42,8 @@ function http_parse_params_split ($string, $delim, $quote = '"') { } function http_parse_params_dequote ($string, $quote = '"') { - if (mb_substr($string, 0, 1) == $quote && mb_substr($string, -1, 1) == $quote && mb_substr($string, -2, 1) != '\\') { - $string = mb_substr($string, 1, -1); + if (mb_substr((string) $string, 0, 1) == $quote && mb_substr((string) $string, -1, 1) == $quote && mb_substr((string) $string, -2, 1) != '\\') { + $string = mb_substr((string) $string, 1, -1); } return $string; } @@ -52,17 +52,17 @@ function http_parse_params ($params) { $result = []; $preresult = []; - $params = str_replace("\\\"", "\0\0", $params); + $params = str_replace("\\\"", "\0\0", (string) $params); foreach (http_parse_params_split($params, ';') as $p0) { foreach (http_parse_params_split($p0, ',') as $p1) { foreach (http_parse_params_split($p1, ' ') as $param) { - $pos = mb_strpos($param, '='); + $pos = mb_strpos((string) $param, '='); if ($pos === false) { - $result[] = str_replace("\0\0", "\"", http_parse_params_dequote(trim($param))); + $result[] = str_replace("\0\0", "\"", (string) http_parse_params_dequote(trim((string) $param))); } else { - $result[][trim(mb_substr($param,0,$pos))] = str_replace("\0\0", "\"", http_parse_params_dequote(trim(mb_substr($param,$pos+1)))); + $result[][trim(mb_substr((string) $param,0,$pos))] = str_replace("\0\0", "\"", (string) http_parse_params_dequote(trim(mb_substr((string) $param,$pos+1)))); } } } @@ -143,9 +143,9 @@ if (!function_exists('http_build_url')) : * @test ["http://example.org/orig?q=1#f","https://www.example.com:9999/replaced#n",0] >>> "https://www.example.com:9999/replaced?q=1#n" * @test ["http://user@www.example.com/pub/index.php?a=b#files",{"scheme":"ftp","host":"ftp.example.com","path":"files/current/","query":"a=c"},271] >>> "ftp://ftp.example.com/pub/files/current/?a=c" --- replace scheme, host, path, query == HTTP_URL_STRIP_FRAGMENT|HTTP_URL_STRIP_AUTH|HTTP_URL_JOIN_QUERY|HTTP_URL_JOIN_PATH */ -function http_build_url($url, $parts = [], $flags = HTTP_URL_FROM_ENV, &$new_url = []) { - is_array($url) || $url = parse_url($url); - is_array($parts) || $parts = parse_url($parts); +function http_build_url(mixed $url, $parts = [], $flags = HTTP_URL_FROM_ENV, &$new_url = []) { + is_array($url) || $url = parse_url((string) $url); + is_array($parts) || $parts = parse_url((string) $parts); isset($url['query']) && is_string($url['query']) || $url['query'] = null; isset($parts['query']) && is_string($parts['query']) || $parts['query'] = null; @@ -157,33 +157,33 @@ function http_build_url($url, $parts = [], $flags = HTTP_URL_FROM_ENV, &$new_url $new_url['port'] = 0; if(!($flags & HTTP_URL_STRIP_PORT)) { - $new_url['port'] = isset($parts['port']) ? $parts['port'] : (isset($url['port']) ? $url['port'] : 0); + $new_url['port'] = $parts['port'] ?? $url['port'] ?? 0; } if(!($flags & HTTP_URL_STRIP_USER)) { - $new_url['user'] = isset($parts['user']) ? $parts['user'] : (isset($url['user']) ? $url['user'] : null); + $new_url['user'] = $parts['user'] ?? $url['user'] ?? null; } if(!($flags & HTTP_URL_STRIP_PASS)) { - $new_url['pass'] = isset($parts['pass']) ? $parts['pass'] : (isset($url['pass']) ? $url['pass'] : null); + $new_url['pass'] = $parts['pass'] ?? $url['pass'] ?? null; } - $new_url['scheme'] = isset($parts['scheme']) ? $parts['scheme'] : (isset($url['scheme']) ? $url['scheme'] : null); - $new_url['host'] = isset($parts['host']) ? $parts['host'] : (isset($url['host']) ? $url['host'] : null); + $new_url['scheme'] = $parts['scheme'] ?? $url['scheme'] ?? null; + $new_url['host'] = $parts['host'] ?? $url['host'] ?? null; if(!($flags & HTTP_URL_STRIP_PATH)) { - if(($flags & HTTP_URL_JOIN_PATH) && isset($url['path']) && isset($parts['path']) && substr($parts['path'], 0, 1) != '/'){ - $new_url['path'] = substr($url['path'], -1) == '/' ? $url['path'] : (dirname($url['path']) . '/'); + if(($flags & HTTP_URL_JOIN_PATH) && isset($url['path']) && isset($parts['path']) && !str_starts_with((string) $parts['path'], '/')){ + $new_url['path'] = str_ends_with((string) $url['path'], '/') ? $url['path'] : (dirname((string) $url['path']) . '/'); $new_url['path'] .= $parts['path']; } else { - $new_url['path'] = isset($parts['path']) ? $parts['path'] : (isset($url['path']) ? $url['path'] : null); + $new_url['path'] = $parts['path'] ?? $url['path'] ?? null; } } if (!($flags & HTTP_URL_STRIP_QUERY)) { if(($flags & HTTP_URL_JOIN_QUERY) && isset($url['query']) && isset($parts['query'])) { - parse_str($url['query'], $url_query); - parse_str($parts['query'], $parts_query); + parse_str((string) $url['query'], $url_query); + parse_str((string) $parts['query'], $parts_query); $new_url['query'] = http_build_query( array_replace_recursive( $url_query, @@ -191,31 +191,21 @@ function http_build_url($url, $parts = [], $flags = HTTP_URL_FROM_ENV, &$new_url ) ); } else { - $new_url['query'] = isset($parts['query']) ? $parts['query'] : (isset($url['query']) ? $url['query'] : null); + $new_url['query'] = $parts['query'] ?? $url['query'] ?? null; } } if(!($flags & HTTP_URL_STRIP_FRAGMENT)) { - $new_url['fragment'] = isset($parts['fragment']) ? $parts['fragment'] : (isset($url['fragment']) ? $url['fragment'] : null); + $new_url['fragment'] = $parts['fragment'] ?? $url['fragment'] ?? null; } if (!isset($new_url['scheme'])) { if ($flags & HTTP_URL_FROM_ENV) { - switch(true) { - case $new_url['port'] == 443: - case !!$_SERVER['HTTPS'] && !strcasecmp($_SERVER['HTTPS'], 'ON'): - $new_url['scheme'] = 'https'; - break; - case !isset($new_url['port']): - case $new_url['port'] == 80: - case $new_url['port'] == 0: - default: - $new_url['scheme'] = 'http'; - break; - case !!($scheme = getservbyport($new_url['port'], "tcp")): - $new_url['scheme'] = $scheme; - break; - } + $new_url['scheme'] = match (true) { + $new_url['port'] == 443, !!$_SERVER['HTTPS'] && !strcasecmp((string) $_SERVER['HTTPS'], 'ON') => 'https', + !!($scheme = getservbyport($new_url['port'], "tcp")) => $scheme, + default => 'http', + }; } else { $new_url['scheme'] = 'http'; } @@ -223,7 +213,7 @@ function http_build_url($url, $parts = [], $flags = HTTP_URL_FROM_ENV, &$new_url if(!isset($new_url['host'])) { if($flags & HTTP_URL_FROM_ENV) { - if(strlen($host = $_SERVER['HTTP_HOST']) || strlen($host = $_SERVER['SERVER_NAME'])) { + if(strlen((string) ($host = $_SERVER['HTTP_HOST'])) || strlen((string) ($host = $_SERVER['SERVER_NAME']))) { $new_url['host'] = $host; } else { $new_url['host'] = php_uname('n'); @@ -234,21 +224,21 @@ function http_build_url($url, $parts = [], $flags = HTTP_URL_FROM_ENV, &$new_url } if(!isset($new_url['path'])) { - if(($flags & HTTP_URL_FROM_ENV) && strlen($_SERVER['REQUEST_URI'])) { - $pos = strpos($_SERVER['REQUEST_URI'], '?'); + if(($flags & HTTP_URL_FROM_ENV) && strlen((string) $_SERVER['REQUEST_URI'])) { + $pos = strpos((string) $_SERVER['REQUEST_URI'], '?'); if($pos !== false) { - $new_url['path'] = substr($_SERVER['REQUEST_URI'], 0, $pos); + $new_url['path'] = substr((string) $_SERVER['REQUEST_URI'], 0, $pos); } else { $new_url['path'] = $_SERVER['REQUEST_URI']; } } else { $new_url['path'] = '/'; } - } elseif (substr($new_url['path'], 0, 1) != '/') { - if(($flags & HTTP_URL_FROM_ENV) && strlen($_SERVER['REQUEST_URI'])) { - $pos = strrpos($_SERVER['REQUEST_URI'], '/'); + } elseif (!str_starts_with((string) $new_url['path'], '/')) { + if(($flags & HTTP_URL_FROM_ENV) && strlen((string) $_SERVER['REQUEST_URI'])) { + $pos = strrpos((string) $_SERVER['REQUEST_URI'], '/'); if($pos !== false) { - $path = substr($_SERVER['REQUEST_URI'], 0, $pos); + $path = substr((string) $_SERVER['REQUEST_URI'], 0, $pos); } else { $path = '/'; } @@ -259,11 +249,11 @@ function http_build_url($url, $parts = [], $flags = HTTP_URL_FROM_ENV, &$new_url } if($new_url['path'] != '/'){ - while(preg_match('#/\.(/|$)#' . REGEX_MOD, $new_url['path'], $matches, PREG_OFFSET_CAPTURE)) { - $new_url['path'] = substr_replace($new_url['path'], "/", $matches[0][1], strlen($matches[0][0])); + while(preg_match('#/\.(/|$)#' . REGEX_MOD, (string) $new_url['path'], $matches, PREG_OFFSET_CAPTURE)) { + $new_url['path'] = substr_replace((string) $new_url['path'], "/", $matches[0][1], strlen($matches[0][0])); } - while(preg_match('#(^|/[^/]+)/\.\.(/|$)#' . REGEX_MOD, $new_url['path'], $matches, PREG_OFFSET_CAPTURE)) { - $new_url['path'] = substr_replace($new_url['path'], "/", $matches[0][1], strlen($matches[0][0])); + while(preg_match('#(^|/[^/]+)/\.\.(/|$)#' . REGEX_MOD, (string) $new_url['path'], $matches, PREG_OFFSET_CAPTURE)) { + $new_url['path'] = substr_replace((string) $new_url['path'], "/", $matches[0][1], strlen($matches[0][0])); } } diff --git a/lib/Common/functions.imageswork.inc b/lib/Common/functions.imageswork.inc index 603db51c..8f1e5a6f 100644 --- a/lib/Common/functions.imageswork.inc +++ b/lib/Common/functions.imageswork.inc @@ -15,7 +15,7 @@ function GDInDrawPicture($src_im, &$dst_im) { } function FindMagicTools($component){ - ($magicPath = FindSystemFile($component)) or FatalError('Cant find ImageMagick '.strtoupper($component).' tools'); + ($magicPath = FindSystemFile($component)) or FatalError('Cant find ImageMagick '.strtoupper((string) $component).' tools'); return $magicPath; } @@ -88,7 +88,7 @@ function GDImageResize($src, $dest, $width, $height, $rgb=0xFFFFFF, $quality=100 // Определяем исходный формат по MIME-информации, предоставленной // функцией getimagesize, и выбираем соответствующую формату // imagecreatefrom-функцию. - $format = strtolower(substr($size['mime'], strpos($size['mime'], '/')+1)); + $format = strtolower(substr((string) $size['mime'], strpos((string) $size['mime'], '/')+1)); $icfunc = "imagecreatefrom" . $format; if (!function_exists($icfunc)) { FatalError('Can\'t find function '.$icfunc); @@ -128,8 +128,8 @@ function GDImageResize($src, $dest, $width, $height, $rgb=0xFFFFFF, $quality=100 function MagicResize($im_filename, $x, $y) { $oldName = $im_filename; - if (substr($im_filename, -4) === '.JPG') { - $im_filename = str_replace('.JPG', '.jpg', $im_filename); + if (str_ends_with((string) $im_filename, '.JPG')) { + $im_filename = str_replace('.JPG', '.jpg', (string) $im_filename); rename($oldName, $im_filename); } @@ -146,8 +146,8 @@ function MagicResize($im_filename, $x, $y) { function NConvertSafeImageResize($im_filename, $x, $y) { $oldName = $im_filename; - if (substr($im_filename, -4) === '.JPG') { - $im_filename = str_replace(".JPG", ".jpg", $im_filename); + if (str_ends_with((string) $im_filename, '.JPG')) { + $im_filename = str_replace(".JPG", ".jpg", (string) $im_filename); rename($oldName, $im_filename); } @@ -192,51 +192,27 @@ function GetImageProperty($file) { $retArray['width'] = $size[0]; $retArray['height'] = $size[1]; - switch($size[2]) { - case '1': - $type = 'GIF'; - break; - case '2': - $type = 'JPG'; - break; - case '3': - $type = 'PNG'; - break; - case '5': - $type = 'PSD'; - break; - case '6': - $type = 'BMP'; - break; - case '7': - case '8': - $type = 'TIFF'; - break; - case '9': - $type = 'JPC'; - break; - case '10': - $type = 'JP2'; - break; - case '11': - $type = 'JPX'; - break; - default: - $type = 'undefined'; - break; - } + $type = match ($size[2]) { + '1' => 'GIF', + '2' => 'JPG', + '3' => 'PNG', + '5' => 'PSD', + '6' => 'BMP', + '7', '8' => 'TIFF', + '9' => 'JPC', + '10' => 'JP2', + '11' => 'JPX', + default => 'undefined', + }; $retArray['type'] = $type; $byteSize = filesize($file); $retArray['byteSize'] = $byteSize; if ($byteSize < 1024) { $retArray['xByteSize'] = $byteSize.' байт'; - } elseif ($byteSize < 1048576) { + } elseif ($byteSize < 1_048_576) { $retArray['xByteSize'] = sprintf("%01.2f", ($byteSize/1024)).' Кбайт'; } else { - $retArray['xByteSize'] = sprintf("%01.2f", ($byteSize/1048576)).' Кбайт'; + $retArray['xByteSize'] = sprintf("%01.2f", ($byteSize/1_048_576)).' Кбайт'; } return $retArray; } - - -?> diff --git a/lib/Common/functions.path.inc b/lib/Common/functions.path.inc index 5dcc020f..35f5c95d 100644 --- a/lib/Common/functions.path.inc +++ b/lib/Common/functions.path.inc @@ -11,11 +11,9 @@ * @test [""] >>> "" */ function path_clear($path) { - $pathItems = explode(DIRECTORY_SEPARATOR, $path); + $pathItems = explode(DIRECTORY_SEPARATOR, (string) $path); - $pathItems = array_map(function ($item) { - return path_filter_filename((string)$item); - }, $pathItems); + $pathItems = array_map(fn($item) => path_filter_filename((string)$item), $pathItems); $pathItems = array_filter($pathItems); @@ -78,7 +76,7 @@ function path_resolve($from, $to = null) { } foreach ($paths as $path) { - $parts = explode('/', rtrim($path, '/')); + $parts = explode('/', rtrim((string) $path, '/')); if (path_is_absolute($path)) { $res = []; } @@ -96,7 +94,7 @@ function path_resolve($from, $to = null) { } $lastPath = end($paths); - $lastSlash = $lastPath[strlen($lastPath)-1] === '/'; + $lastSlash = $lastPath[strlen((string) $lastPath)-1] === '/'; array_unshift($res, ''); $lastSlash && array_push($res, ''); @@ -135,7 +133,7 @@ function path_relative($from, $to) { array_push($outputParts, '..'); } - $outputParts = array_merge($outputParts, array_slice($toParts, $samePartsLength)); + $outputParts = [...$outputParts, ...array_slice($toParts, $samePartsLength)]; return join('/', $outputParts); } diff --git a/lib/Common/functions.pp.inc b/lib/Common/functions.pp.inc index d530b42b..ccf9fa45 100644 --- a/lib/Common/functions.pp.inc +++ b/lib/Common/functions.pp.inc @@ -50,7 +50,7 @@ function pp_base_paths($sub = null, $withPlugins = false) { * @return string pathname with the highest order * @todo optimize me. dont look file in unexistant paths at least */ -function pp_fetch_file($where, $what, $postfix = '', $ext = '.inc') { +function pp_fetch_file(string|array $where, string|array $what, string|array $postfix = '', $ext = '.inc') { $ext = (isset($ext[0]) && $ext[0] == '.' ? '' : '.') . $ext; $where = (array)($where); @@ -74,7 +74,7 @@ function pp_fetch_file($where, $what, $postfix = '', $ext = '.inc') { } foreach ($what as $file) { foreach ($postfix as $p) { - if (mb_substr($file, -4) != $p . $ext) { + if (mb_substr((string) $file, -4) != $p . $ext) { $filenames = [ $path . $file . $p . $ext, $path . $file @@ -107,7 +107,7 @@ function pp_plugins() { $result[] = [ 'tag' => $tag, 'name' => $plugin->getName(), - 'path' => pathinfo($plugin->getPathToPlugin(), PATHINFO_DIRNAME) . DIRECTORY_SEPARATOR, + 'path' => pathinfo((string) $plugin->getPathToPlugin(), PATHINFO_DIRNAME) . DIRECTORY_SEPARATOR, ]; } return $result; diff --git a/lib/Common/functions.string.inc b/lib/Common/functions.string.inc index 21f9c087..bd59b7f4 100644 --- a/lib/Common/functions.string.inc +++ b/lib/Common/functions.string.inc @@ -9,14 +9,14 @@ * @subpackage Common */ -function NumericCmp($a, $b) { - if ($a == $b) return 0; - return ($a < $b) ? -1 : 1; +function NumericCmp($a, $b) +{ + return $a <=> $b; } function _StrToLower($text) { - $text = mb_strtolower($text); + $text = mb_strtolower((string) $text); if (defined('MB_DISABLED')) { $trans = ['ю' => 'Ю', 'а' => 'А', 'б' => 'Б', 'ц' => 'Ц', 'д' => 'Д', 'е' => 'Е', 'ё' => 'Ё', 'ф' => 'Ф', 'г' => 'Г', 'х' => 'Х', 'и' => 'И', 'й' => 'Й', 'к' => 'К', 'л' => 'Л', 'м' => 'М', 'н' => 'Н', 'о' => 'О', 'п' => 'П', 'я' => 'Я', 'р' => 'Р', 'с' => 'С', 'т' => 'Т', 'у' => 'У', 'ж' => 'Ж', 'в' => 'В', 'ь' => 'Ь', 'ы' => 'Ы', 'з' => 'З', 'ш' => 'Ш', 'э' => 'Э', 'щ' => 'Щ', 'ч' => 'Ч', 'ъ' => 'Ъ']; $trans = array_flip($trans); @@ -26,7 +26,7 @@ function _StrToLower($text) { } function _StrToUpper($text) { - $text = mb_strtoupper($text); + $text = mb_strtoupper((string) $text); if (defined('MB_DISABLED')) { $trans = ['ю' => 'Ю', 'а' => 'А', 'б' => 'Б', 'ц' => 'Ц', 'д' => 'Д', 'е' => 'Е', 'Ё' => 'ё', 'ф' => 'Ф', 'г' => 'Г', 'х' => 'Х', 'и' => 'И', 'й' => 'Й', 'к' => 'К', 'л' => 'Л', 'м' => 'М', 'н' => 'Н', 'о' => 'О', 'п' => 'П', 'я' => 'Я', 'р' => 'Р', 'с' => 'С', 'т' => 'Т', 'у' => 'У', 'ж' => 'Ж', 'в' => 'В', 'ь' => 'Ь', 'ы' => 'Ы', 'з' => 'З', 'ш' => 'Ш', 'э' => 'Э', 'щ' => 'Щ', 'ч' => 'Ч', 'ъ' => 'Ъ']; $text = mb_strtr($text, $trans); @@ -84,10 +84,10 @@ function _TranslitFilename($text) { "\n" => '' ]; - $text = mb_strtr(trim($text), $trans); + $text = mb_strtr(trim((string) $text), $trans); // Remove remaining unsafe characters. - $text = preg_replace('![^0-9A-Za-z_.-]!', '', $text); + $text = preg_replace('![^0-9A-Za-z_.-]!', '', (string) $text); // Remove multiple consecutive non-alphabetical characters. $text = preg_replace('/(_)_+|(\.)\.+|(-)-+/', '\\1\\2\\3', $text); // Check if filename is empty or starts with dot @@ -95,7 +95,7 @@ function _TranslitFilename($text) { $text = time() . $text; } - return mb_substr($text, 0, 255); //max filename length in most common filesystems + return mb_substr((string) $text, 0, 255); //max filename length in most common filesystems } function _TranslitUnique($text) { @@ -157,7 +157,7 @@ function _TranslitModern($text) { } function NumericEndingsRussian($count, $one, $two, $zero) { - $i = (int) mb_substr( $count, -1 ); + $i = (int) mb_substr( (string) $count, -1 ); $s = (int) $count; $x = $s; if ( $x > 99 ) { @@ -178,14 +178,14 @@ function htmlspecialcharsArray($array) { if (is_array($v)) { $array[$k] = htmlspecialcharsArray($v); } else { - $array[$k] = htmlspecialchars($v, ENT_COMPAT|ENT_HTML401, DEFAULT_CHARSET); + $array[$k] = htmlspecialchars((string) $v, ENT_COMPAT|ENT_HTML401, DEFAULT_CHARSET); } } return $array; } function DbQuoteString($s) { - return "'".addslashes($s)."'"; + return "'".addslashes((string) $s)."'"; } function Strip1251($s) { @@ -280,7 +280,7 @@ function _strReplaceLimited($search, $replacement, $subject, $limit = 1, $caseMa } function appendParamToUrl($url, $k, $v, $fragment = false, $num_save = false) { - $tmp = parse_url($url); + $tmp = parse_url((string) $url); $url = NULL; if (!empty($tmp['scheme'])) { $url .= $tmp['scheme'].'://'.$tmp['host']; @@ -288,7 +288,7 @@ function appendParamToUrl($url, $k, $v, $fragment = false, $num_save = false) { $url .= PXRequest::GetHttpProto().'://'.PXRequest::GetHttpHost(); } if (!empty($tmp['path'])) { - if ($tmp['path']{0} == '/') { + if ($tmp['path'][0] == '/') { $url .= $tmp['path']; } else { $url .= '/'.$tmp['path']; @@ -307,7 +307,7 @@ function appendParamToUrl($url, $k, $v, $fragment = false, $num_save = false) { if (is_array($qv)) { $url .= multiLevelVarsToString($qk, $qv, null, $num_save) . '&'; } else { - $url .= $qk.'='.urlencode($qv).'&'; + $url .= $qk.'='.urlencode((string) $qv).'&'; } } $url = mb_substr($url, 0, -1); @@ -320,7 +320,7 @@ function appendParamToUrl($url, $k, $v, $fragment = false, $num_save = false) { } function removeParamFromUrl($url, $k, $fragment = false, $removeAll = false, $num_save = false) { - $tmp = parse_url($url); + $tmp = parse_url((string) $url); $url = NULL; if (!empty($tmp['scheme'])) { $url .= $tmp['scheme'].'://'.$tmp['host']; @@ -328,7 +328,7 @@ function removeParamFromUrl($url, $k, $fragment = false, $removeAll = false, $nu $url .= PXRequest::GetHttpProto().'://'.PXRequest::GetHttpHost(); } if (!empty($tmp['path'])) { - if ($tmp['path']{0} == '/') { + if ($tmp['path'][0] == '/') { $url .= $tmp['path']; } else { $url .= '/'.$tmp['path']; @@ -352,7 +352,7 @@ function removeParamFromUrl($url, $k, $fragment = false, $removeAll = false, $nu if (is_array($qv)) { $url .= multiLevelVarsToString($qk, $qv, null, $num_save) . '&'; } else { - $url .= $qk.'='.urlencode($qv).'&'; + $url .= $qk.'='.urlencode((string) $qv).'&'; } } $url = mb_substr($url, 0, -1); @@ -361,20 +361,17 @@ function removeParamFromUrl($url, $k, $fragment = false, $removeAll = false, $nu } function parseStrMagic($str, &$arr) { - mb_parse_str($str, $arr); - if (get_magic_quotes_gpc()) { - $arr = stripSlashesDeep($arr); - } + mb_parse_str((string) $str, $arr); } function stripSlashesDeep($value) { - return is_array($value) ? array_map("stripSlashesDeep", $value) : stripslashes($value); + return is_array($value) ? array_map("stripSlashesDeep", $value) : stripslashes((string) $value); } function multiLevelVarsToString($name, $arrayVars, $leaf=null, $num_save=false) { foreach($arrayVars as $k=>$a) { $t[$k] = "{$leaf}[".((is_numeric($k) && !$num_save) ? '' : $k)."]"; - $t[$k] = is_array($a) ? multiLevelVarsToString($name, $a, $t[$k], $num_save) : $name.$t[$k]."=".urlencode($a); + $t[$k] = is_array($a) ? multiLevelVarsToString($name, $a, $t[$k], $num_save) : $name.$t[$k]."=".urlencode((string) $a); } return !empty($t) ? join ('&', $t) : ''; @@ -385,14 +382,14 @@ function generateRandomString($length) { $tmp = "0123456789ABCDEF"; $string = NULL; for($i=0;$i<$length;$i++) { - $string .= $tmp{rand(0,15)}; + $string .= $tmp[random_int(0,15)]; } return $string; } // NOTICE: no need here to use mb_ functions here function isUtfString($str) { - $len = strlen($str); + $len = strlen((string) $str); for ($i = 0; $i < $len; $i++) { $cp = ord($str[$i]); if ($cp >= 0x00 && $cp <= 0x7F) continue; # 00 - 7F -- 1 byte ascii @@ -417,7 +414,7 @@ function isUtfString($str) { // no need here to use preg_ with UTF-8 modifier function isAsciiString($AStr) { static $ptrASCII = '[\x00-\x7F]'; - return (bool)preg_match("/^{$ptrASCII}*$/sS", $AStr); + return (bool)preg_match("/^{$ptrASCII}*$/sS", (string) $AStr); } /* @@ -434,7 +431,7 @@ function isAsciiString($AStr) { */ // NOTICE: no need to use mb_ functions here function substrUTF8($str, $from, $length) { - $len = strlen($str); + $len = strlen((string) $str); $chars = 0; $i = 0; $out = ''; @@ -481,11 +478,11 @@ function substrUTF8($str, $from, $length) { function wordWrapHtmlSafe($str, $width = 60, $break = "\n", $nobreak = "", $nobr = "pre", $utf = false) { // Split HTML content into an array delimited by < and > // The flags save the delimeters and remove empty variables - $content = preg_split("/([<>])/".REGEX_MOD, $str, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $content = preg_split("/([<>])/".REGEX_MOD, (string) $str, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); // Transform protected element lists into arrays - $nobreak = explode(" ", $nobreak); - $nobr = explode(" ", $nobr); + $nobreak = explode(" ", (string) $nobreak); + $nobr = explode(" ", (string) $nobr); // Variable setup $intag = false; @@ -517,10 +514,10 @@ function wordWrapHtmlSafe($str, $width = 60, $break = "\n", $nobreak = "", $nobr if ($intag) { // If the first character is not a / then this is an opening tag - if ($value{0} != "/") { + if ($value[0] != "/") { // Collect the tag name - preg_match("/^(.*?)(\s|$)/$utf", $value, $t); + preg_match("/^(.*?)(\s|$)/$utf", (string) $value, $t); // If this is a protected element, activate the associated protection flag if ((!count($innbk) && in_array($t[1], $nobreak)) || in_array($t[1], $innbk)) $innbk[] = $t[1]; @@ -530,15 +527,15 @@ function wordWrapHtmlSafe($str, $width = 60, $break = "\n", $nobreak = "", $nobr } else { // If this is a closing tag for a protected element, unset the flag - if (in_array(mb_substr($value, 1), $innbk)) unset($innbk[count($innbk)]); - if (in_array(mb_substr($value, 1), $innbr)) unset($innbr[count($innbr)]); + if (in_array(mb_substr((string) $value, 1), $innbk)) unset($innbk[count($innbk)]); + if (in_array(mb_substr((string) $value, 1), $innbr)) unset($innbr[count($innbr)]); } // Else if we're outside any tags... } else if ($value) { // If unprotected, remove all existing \r, replace all existing \n with \r - if (!count($innbr)) $value = str_replace("\n", "\r", str_replace("\r", "", $value)); + if (!count($innbr)) $value = str_replace("\n", "\r", str_replace("\r", "", (string) $value)); // If unprotected, enter the line-break loop if (!count($innbk)) { @@ -546,10 +543,10 @@ function wordWrapHtmlSafe($str, $width = 60, $break = "\n", $nobreak = "", $nobr $store = $value; // Find the first stretch of characters over the $width limit - if (preg_match("/^(.*?\s|^)(([^\s&]|&(\w{2,5}|#\d{2,4});){".$width."})(?!(".preg_quote($break, "/")."|\s))(.*)$/s$utf", $value, $match)) { + if (preg_match("/^(.*?\s|^)(([^\s&]|&(\w{2,5}|#\d{2,4});){".$width."})(?!(".preg_quote((string) $break, "/")."|\s))(.*)$/s$utf", (string) $value, $match)) { // Determine the last "safe line-break" character within this match - for ($x = 0, $ledge = 0; $x < mb_strlen($lbrks); $x++) $ledge = max($ledge, mb_strrpos($match[2], $lbrks{$x})); + for ($x = 0, $ledge = 0; $x < mb_strlen($lbrks); $x++) $ledge = max($ledge, mb_strrpos($match[2], $lbrks[$x])); if (!$ledge) $ledge = mb_strlen($match[2]) - 1; // Insert the modified string @@ -561,7 +558,7 @@ function wordWrapHtmlSafe($str, $width = 60, $break = "\n", $nobreak = "", $nobr } // If unprotected, replace all \r with
\n to finish - if (!count($innbr)) $value = str_replace("\r", "
\n", $value); + if (!count($innbr)) $value = str_replace("\r", "
\n", (string) $value); } } @@ -584,10 +581,10 @@ function fixWordLength($string, $length = 10) { $entityPattern = '/(&#\d+;|&\w+;)/'.REGEX_MOD; $entityReplacement = chr(2); - preg_match_all($entityPattern, $string, $replacement); + preg_match_all($entityPattern, (string) $string, $replacement); $replacement = current($replacement); - $string = preg_replace($entityPattern, $entityReplacement, $string); + $string = preg_replace($entityPattern, $entityReplacement, (string) $string); $hyphens = $string; $hyphens = preg_replace('/([\s,.-])/i'.REGEX_MOD, '$1' . $doubleDelim, $hyphens); @@ -660,7 +657,7 @@ function utf8_urldecode($p) { ]; $hex = $p[1]; - $dec = hexdec($hex); + $dec = hexdec((string) $hex); if (is_callable('iconv')) { $c = @iconv(CHARSET_UCS2BE, DEFAULT_CHARSET, pack('n', $dec)); @@ -689,7 +686,7 @@ function utf8_urldecode($p) { if (!function_exists("lcfirst")) { function lcfirst($string) { - $string{0} = strtolower($string{0}); + $string[0] = strtolower((string) $string[0]); return $string; } } @@ -703,7 +700,8 @@ function quot($string, $double = true) { * @author runcore */ function money2str($money, $stripkop=false) { - $nol = 'ноль'; + $str = []; + $nol = 'ноль'; $str[100]= ['','сто','двести','триста','четыреста','пятьсот','шестьсот', 'семьсот', 'восемьсот','девятьсот']; $str[11] = ['','десять','одиннадцать','двенадцать','тринадцать', 'четырнадцать','пятнадцать','шестнадцать','семнадцать', 'восемнадцать','девятнадцать','двадцать']; $str[10] = ['','десять','двадцать','тридцать','сорок','пятьдесят', 'шестьдесят','семьдесят','восемьдесят','девяносто']; @@ -722,11 +720,11 @@ function money2str($money, $stripkop=false) { $out = $tmp = []; $o = []; // Поехали! - $tmp = explode('.', str_replace(',','.', $money)); + $tmp = explode('.', str_replace(',','.', (string) $money)); $rub = number_format($tmp[ 0], 0,'','-'); if ($rub== 0) $out[] = $nol; // нормализация копеек - $kop = isset($tmp[1]) ? mb_substr(mb_str_pad($tmp[1], 2, '0', STR_PAD_RIGHT), 0,2) : '00'; + $kop = isset($tmp[1]) ? mb_substr((string) mb_str_pad($tmp[1], 2, '0', STR_PAD_RIGHT), 0,2) : '00'; $segments = explode('-', $rub); $offset = sizeof($segments); if ((int)$rub== 0) { // если 0 рублей @@ -744,9 +742,9 @@ function money2str($money, $stripkop=false) { // нормализация $ri = mb_str_pad($ri, 3, '0', STR_PAD_LEFT); // получаем циферки для анализа - $r1 = (int)mb_substr($ri, 0,1); //первая цифра - $r2 = (int)mb_substr($ri,1,1); //вторая - $r3 = (int)mb_substr($ri,2,1); //третья + $r1 = (int)mb_substr((string) $ri, 0,1); //первая цифра + $r2 = (int)mb_substr((string) $ri,1,1); //вторая + $r3 = (int)mb_substr((string) $ri,2,1); //третья $r22= (int)$r2.$r3; //вторая и третья // разгребаем порядки if ($ri>99) $o[] = $str[100][$r1]; // Сотни @@ -773,41 +771,41 @@ function money2str($money, $stripkop=false) { // Converts "abc_def", "abc-def", "abcDef" strings to "AbcDef", but not "ab-cDef" function convertStringToPascalCase ($s) { - return ucfirst(convertStringToCamelCase($s)); + return ucfirst((string) convertStringToCamelCase($s)); } // Converts "AbcDef" string to "abcDef" function convertStringToCamelCase ($s) { - return preg_replace_callback('/[\-_][a-z]/i'.REGEX_MOD, '__convertStringToCamelCase_helper', $s); + return preg_replace_callback('/[\-_][a-z]/i'.REGEX_MOD, '__convertStringToCamelCase_helper', (string) $s); } -function __convertStringToCamelCase_helper($a) { return mb_strtoupper($a[0][1]); } +function __convertStringToCamelCase_helper($a) { return mb_strtoupper((string) $a[0][1]); } // Converts "AbcDef" string to "abc_def" function convertStringToUnderscored ($s) { - return ltrim(preg_replace_callback('/(?:[A-Z]|(?:-)[a-z])/'.REGEX_MOD, '__convertStringToUnderscored_helper', $s), '_'); + return ltrim(preg_replace_callback('/(?:[A-Z]|(?:-)[a-z])/'.REGEX_MOD, '__convertStringToUnderscored_helper', (string) $s), '_'); } -function __convertStringToUnderscored_helper($a) { return '_' . mb_strtolower(substr($a[0], -1, 1)); } +function __convertStringToUnderscored_helper($a) { return '_' . mb_strtolower(substr((string) $a[0], -1, 1)); } // Converts "AbcDef" string to "abc-def" function convertStringToDashed ($s) { - return ltrim(preg_replace_callback('/(?:[A-Z]|(?:_)[a-z])/'.REGEX_MOD, '__convertStringToDashed_helper', $s), '-'); + return ltrim(preg_replace_callback('/(?:[A-Z]|(?:_)[a-z])/'.REGEX_MOD, '__convertStringToDashed_helper', (string) $s), '-'); } -function __convertStringToDashed_helper($a) { return '-' . mb_strtolower(substr($a[0], -1, 1)); } +function __convertStringToDashed_helper($a) { return '-' . mb_strtolower(substr((string) $a[0], -1, 1)); } function _stripBadFileChars($fname) { - return str_replace(['..' . DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, chr(0)], '', $fname); + return str_replace(['..' . DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, chr(0)], '', (string) $fname); } function softcut ($string, $max_length, $tail = null) { - if (mb_strlen($string) < $max_length) { + if (mb_strlen((string) $string) < $max_length) { return $string; } if (is_null($tail)) { $tail = '...'; } - $tail_length = mb_strlen($tail); - $swot = mb_substr($string, 0, $max_length - $tail_length); // string that shorter than max_length without tail width + $tail_length = mb_strlen((string) $tail); + $swot = mb_substr((string) $string, 0, $max_length - $tail_length); // string that shorter than max_length without tail width $cut_pos = 0; $delimiters = [' ', ',', '.', '"', "'", '/', ';', '-', '+', "\t"]; @@ -815,6 +813,6 @@ function softcut ($string, $max_length, $tail = null) { $cut_pos = max($cut_pos, mb_strrpos($swot,$d)); } - $string = mb_substr($string, 0, $cut_pos); + $string = mb_substr((string) $string, 0, $cut_pos); return $string . $tail; } diff --git a/lib/Common/functions.test.inc b/lib/Common/functions.test.inc index d5f65d6b..188647e3 100644 --- a/lib/Common/functions.test.inc +++ b/lib/Common/functions.test.inc @@ -4,7 +4,7 @@ * Helpers for testing */ -define('TEST_CASE_NAME_LENGTH', ((int)($w = trim(`tput cols`)) ? $w : 80) - 36); +define('TEST_CASE_NAME_LENGTH', ((int)($w = trim((string) `tput cols`)) ? $w : 80) - 36); /** * unit test worker @@ -30,7 +30,7 @@ function t($name, $callback, $expected = true) { : json_encode($result) === json_encode($expected); // todo: make it more flexible $nameLength = TEST_CASE_NAME_LENGTH; - $case = sprintf("%-{$nameLength}s", (mb_strlen($name) > $nameLength ? mb_substr($name, 0, $nameLength-1) . 'Б─╕' : $name)); + $case = sprintf("%-{$nameLength}s", (mb_strlen((string) $name) > $nameLength ? mb_substr((string) $name, 0, $nameLength-1) . 'Б─╕' : $name)); con_printf('%3d. %s %+16s', $counter, $case, number_format($elapsedTime * 1000, 4, ',', ' ').'ms '); con_printf(sprintf('[%s{end}]{nl}', $test ? '{fg:lgreen} OK ' : '{fg:lred}FAIL')); @@ -83,7 +83,7 @@ function tt ($file) { echo "Aim \"{$file}\" not found\n"; return; } - $toks = token_get_all(file_get_contents($file)); + $toks = \PhpToken::tokenize(file_get_contents($file)); // handle functions and classes $ns = null; $class = null; @@ -130,10 +130,8 @@ function _tt_function($aim) { thead($method->getName()); foreach ($tests as $k => $test) { $res = array_pop($test); - $case = trim($k); - t($case, function () use ($methodname, $test) { - return call_user_func_array($methodname, $test); - }, $res); + $case = trim((string) $k); + t($case, fn() => call_user_func_array($methodname, $test), $res); } } @@ -149,10 +147,8 @@ function _tt_class($aim) { thead($cref->getName(), $method->getName()); foreach ($tests as $k => $test) { $res = array_pop($test); - $case = trim($k); - t($case, function () use ($methodname, $test) { - return call_user_func_array($methodname, $test); - }, $res); + $case = trim((string) $k); + t($case, fn() => call_user_func_array($methodname, $test), $res); } } } @@ -160,21 +156,19 @@ function _tt_class($aim) { function _parsePhpDocTest($phpdoc) { $tests = []; $pos = 0; - while ($pos = mb_strpos($phpdoc, '@test ', $pos)) { - $end = mb_strpos($phpdoc, "\n", $pos + 1); + while ($pos = mb_strpos((string) $phpdoc, '@test ', $pos)) { + $end = mb_strpos((string) $phpdoc, "\n", $pos + 1); //$end = strpos($phpdoc, '@test', $pos + 1) ?: strpos($phpdoc, '@', $pos + 1) ?: strpos($phpdoc, '**/', $pos + 1); - ($end === false) && $end = mb_strlen($phpdoc); + ($end === false) && $end = mb_strlen((string) $phpdoc); - $data = mb_substr($phpdoc, $pos + 5, $end - $pos - 5); + $data = mb_substr((string) $phpdoc, $pos + 5, $end - $pos - 5); [$expression, $title] = array_pad(explode('---', $data), 2, null); - [$params, $result] = array_pad(explode('>>>', $expression), 2, 'null'); - $test = (array)json_decode(trim($params), 1); - preg_match('@^/.+/[ism]?$@s', trim($result), $m); + [$params, $result] = array_pad(explode('>>>', (string) $expression), 2, 'null'); + $test = (array)json_decode(trim((string) $params), true); + preg_match('@^/.+/[ism]?$@s', trim((string) $result), $m); $test[] = !$m? - json_decode(trim($result)) - : function ($expected) use ($m) { - return preg_match($m[0], $expected)? $expected : $m[0]; - }; + json_decode(trim((string) $result)) + : fn($expected) => preg_match($m[0], (string) $expected)? $expected : $m[0]; $tests[$title ?: $data] = $test; $pos ++; } diff --git a/lib/Common/functions.validate.inc b/lib/Common/functions.validate.inc index 1630a03a..47169919 100644 --- a/lib/Common/functions.validate.inc +++ b/lib/Common/functions.validate.inc @@ -1,6 +1,6 @@ diff --git a/lib/Config/Description/Bindings/struct-match.class.inc b/lib/Config/Description/Bindings/struct-match.class.inc index f7a5b892..dded2b9b 100644 --- a/lib/Config/Description/Bindings/struct-match.class.inc +++ b/lib/Config/Description/Bindings/struct-match.class.inc @@ -1,8 +1,7 @@ content[$this->var]) && preg_match($this->value.REGEX_MOD, $current->content[$this->var]); + return PXBindingDescription::bindingRule($content, $current) && + isset($current->content[$this->var]) && preg_match($this->value.REGEX_MOD, (string) $current->content[$this->var]); } } -?> diff --git a/lib/Config/Description/db.class.inc b/lib/Config/Description/db.class.inc index 6a4b4754..db870536 100644 --- a/lib/Config/Description/db.class.inc +++ b/lib/Config/Description/db.class.inc @@ -37,7 +37,7 @@ class NLDBDescription { $q = []; $r = parse_url(EnvLoader::get('DATABASE_DSN')); - parse_str(getFromArray($r, 'query', ''), $q); + parse_str((string) getFromArray($r, 'query', ''), $q); // either cache is supplied via &cache parameter of DATABASE_DSN // or cache is defined as additional environment variable. @@ -51,7 +51,7 @@ class NLDBDescription 'dbtype' => getFromArray($r, 'scheme', ''), 'user' => getFromArray($r, 'user', ''), 'password' => getFromArray($r, 'pass', ''), - 'dbname' => ltrim(getFromArray($r, 'path', ''), '/'), + 'dbname' => ltrim((string) getFromArray($r, 'path', ''), '/'), 'host' => getFromArray($r, 'host', ''), 'port' => getFromArray($r, 'port', ''), 'encoding' => getFromArray($q, 'encoding', DEFAULT_CHARSET), @@ -89,14 +89,10 @@ class NLDBDescription { $db = null; - switch ($this->dbtype) { - case PostgreSqlDriver::TYPE: - $db = new PostgreSqlDriver($this); - break; - - default: - throw new DatabaseException("Can't find class: {$this->dbtype}"); - } + $db = match ($this->dbtype) { + PostgreSqlDriver::TYPE => new PostgreSqlDriver($this), + default => throw new DatabaseException("Can't find class: {$this->dbtype}"), + }; $db->setCache($this->cache); return $db; diff --git a/lib/Config/Description/directory.class.inc b/lib/Config/Description/directory.class.inc index aa25d83c..7f73a7d3 100644 --- a/lib/Config/Description/directory.class.inc +++ b/lib/Config/Description/directory.class.inc @@ -9,7 +9,6 @@ use PP\Lib\Datastruct\Tree; * @subpackage Application */ class PXDirectoryDescription { - public $name; public $load; public $loaded; public $schema; @@ -26,8 +25,7 @@ class PXDirectoryDescription { public $sourceItem; public $sourceDataFields; - public function __construct($name) { - $this->name = $name; + public function __construct(public $name) { $this->values = []; $this->filter = []; $this->loaded = false; @@ -47,7 +45,7 @@ class PXDirectoryDescription { public function GetTree($saveOrphans = false) { $retArray = []; - if (count($this->values) && $this->parentField !== false) { + if ((is_countable($this->values) ? count($this->values) : 0) && $this->parentField !== false) { $retArray = new Tree($this->values, 'id', $this->parentField, 'title', $saveOrphans); } diff --git a/lib/Config/Description/field.class.inc b/lib/Config/Description/field.class.inc index 9dfdab38..bb8ee436 100644 --- a/lib/Config/Description/field.class.inc +++ b/lib/Config/Description/field.class.inc @@ -24,13 +24,11 @@ class PXFieldDescription public $defaultValue = NULL; public $listed = NULL; public $noindex = NULL; - public $typeDescription; public $comments = ''; public $groupName = NULL; - public function __construct($fieldNode, $app, $type) + public function __construct($fieldNode, $app, public $typeDescription) { - $this->typeDescription = $type; $this->app = $app; if (is_object($fieldNode)) { @@ -56,7 +54,7 @@ class PXFieldDescription break; case 'listed': - $listed = mb_strtoupper($value); + $listed = mb_strtoupper((string) $value); $this->listed = $listed == 'TRUE'; break; @@ -71,7 +69,7 @@ class PXFieldDescription break; case 'noindex': - $noindex = mb_strtoupper($value); + $noindex = mb_strtoupper((string) $value); $this->noindex = $noindex == 'TRUE'; break; @@ -111,7 +109,7 @@ class PXFieldDescription foreach ($defaultValueNode as $value) { $value = $value->nodeValue(); $value = pp_simplexml_decode_string($value); - $value = trim($value); + $value = trim((string) $value); $this->defaultValue .= preg_replace('/^\t+/m' . REGEX_MOD, '', $value); } diff --git a/lib/Config/Description/module.class.inc b/lib/Config/Description/module.class.inc index 57fa8750..01f169ab 100644 --- a/lib/Config/Description/module.class.inc +++ b/lib/Config/Description/module.class.inc @@ -98,7 +98,7 @@ class PXModuleDescription { * @return string|null */ public function getSetting($setting) { - return isset($this->settings[$setting])? $this->settings[$setting]: null; + return $this->settings[$setting] ?? null; } /** @@ -119,4 +119,3 @@ class PXModuleDescription { } } - diff --git a/lib/Config/Description/ref.class.inc b/lib/Config/Description/ref.class.inc index e3700e89..b103c86f 100644 --- a/lib/Config/Description/ref.class.inc +++ b/lib/Config/Description/ref.class.inc @@ -59,7 +59,7 @@ class PXRefDescription $ref = new PXRefDescription(); $nodes = $domReference->childNodes(); foreach ($attrs as $attr) { - switch (trim($attr->name)) { + switch (trim((string) $attr->name)) { case 'name': $ref->name = $attr->value; break; diff --git a/lib/Config/Description/trigger.class.inc b/lib/Config/Description/trigger.class.inc index d627e4fb..59230554 100644 --- a/lib/Config/Description/trigger.class.inc +++ b/lib/Config/Description/trigger.class.inc @@ -13,10 +13,7 @@ class PXTriggerDescription private $instance = NULL; private $folder = NULL; - /** - * @return PXAbstractTrigger|PXAbstractDatabaseTrigger|PXAbstractSystemTrigger - */ - public function getTrigger() + public function getTrigger(): \PXAbstractTrigger|\PXAbstractDatabaseTrigger|\PXAbstractSystemTrigger { if ($this->instance) { return $this->instance; @@ -63,9 +60,9 @@ class PXTriggerDescription include_once $file; } - $this->klass = sprintf('PXTrigger%s%s', ucfirst($this->type), ucfirst($this->name)); + $this->klass = sprintf('PXTrigger%s%s', ucfirst((string) $this->type), ucfirst((string) $this->name)); - if (!(strlen($this->name) && class_exists($this->klass) && is_subclass_of($this->klass, sprintf("PXAbstract%sTrigger", $this->type)))) { + if (!(strlen((string) $this->name) && class_exists($this->klass) && is_subclass_of($this->klass, sprintf("PXAbstract%sTrigger", $this->type)))) { FatalError("Триггер '{$this->klass}' отсутствует или его интерфейс не совпадает с типом '{$this->type}'"); } } @@ -77,5 +74,3 @@ class PXTriggerDescription return pp_fetch_file(['', 'plugins/' . $this->folder], $file, '.trigger'); } } - -?> diff --git a/lib/Config/Description/type.class.inc b/lib/Config/Description/type.class.inc index 49b2a8b8..3719d38e 100644 --- a/lib/Config/Description/type.class.inc +++ b/lib/Config/Description/type.class.inc @@ -57,15 +57,11 @@ class PXTypeDescription { $ac = []; if (is_object($object)) { - $ac = (isset($object->content[OBJ_FIELD_CHILDREN])) - ? $object->content[OBJ_FIELD_CHILDREN] - : []; + $ac = $object->content[OBJ_FIELD_CHILDREN] ?? []; } if (is_array($object)) { - $ac = (isset($object[OBJ_FIELD_CHILDREN])) - ? $object[OBJ_FIELD_CHILDREN] - : []; + $ac = $object[OBJ_FIELD_CHILDREN] ?? []; } foreach ($ac as $k => $v) { @@ -101,7 +97,7 @@ class PXTypeDescription } // static parser factory - public static function fillAppTypes($domDatatypes, &$app) + public static function fillAppTypes($domDatatypes, PXApplication $app) { if (!is_array($domDatatypes) || count($domDatatypes) == 0) { @@ -164,7 +160,7 @@ class PXTypeDescription $type->assignToGroup($field); } - $app->types[$type->id] = $type; + $app->setDataType($type->id, $type); } static::findParents($app->types); @@ -255,18 +251,13 @@ class PXTypeDescription */ public static function _mapByDefaults($string) { - switch ($string) { - case 'all' : - return PP_CHILDREN_FETCH_ALL; - case 'selected': - return PP_CHILDREN_FETCH_SELECTED; - case 'paged' : - return PP_CHILDREN_FETCH_PAGED; - case 'none' : - return PP_CHILDREN_FETCH_NONE; - default : - return NULL; - } + return match ($string) { + 'all' => PP_CHILDREN_FETCH_ALL, + 'selected' => PP_CHILDREN_FETCH_SELECTED, + 'paged' => PP_CHILDREN_FETCH_PAGED, + 'none' => PP_CHILDREN_FETCH_NONE, + default => NULL, + }; } } diff --git a/lib/Config/application.class.inc b/lib/Config/application.class.inc index 296d8aae..a1d34215 100644 --- a/lib/Config/application.class.inc +++ b/lib/Config/application.class.inc @@ -1,17 +1,17 @@ Yaml::parse(file_get_contents($file)), $fileList); foreach ($langList as $data) { $this->appendLang($data); @@ -237,7 +235,7 @@ final class PXApplication */ public function getProperty($k, $defaultValue = null) { - return (isset($this->properties[$k])) ? $this->properties[$k] : $defaultValue; + return $this->properties[$k] ?? $defaultValue; } /** @@ -247,9 +245,7 @@ final class PXApplication */ public function getAvailableModules() { - return array_filter($this->modules, function ($module) { - return PXRegistry::getUser()->can('admin', $module); - }); + return array_filter($this->modules, fn($module) => PXRegistry::getUser()->can('admin', $module)); } /** @@ -275,9 +271,7 @@ final class PXApplication return $availableModules; } - return array_filter($availableModules, function ($module) use ($package) { - return 0 === strpos($module->package, $package); - }); + return array_filter($availableModules, fn($module) => str_starts_with((string) $module->package, $package)); } /** @@ -311,10 +305,9 @@ final class PXApplication } /** - * @param string $formatName - * @return array + * {@inheritDoc} */ - public function initContentObject($formatName) + public function initContentObject(string $formatName): array { $object = []; @@ -389,23 +382,23 @@ final class PXApplication $curDir->source = $domDirectory->getAttribute('source'); $curDir->sourceDir = dirname($fileName) . DIRECTORY_SEPARATOR; - $displayfield = trim($domDirectory->getAttribute('displayfield')); + $displayfield = trim((string) $domDirectory->getAttribute('displayfield')); $curDir->displayField = strlen($displayfield) ? $displayfield : 'title'; $location = $domDirectory->getAttribute('location'); $curDir->location = ($location != '') ? $location : NULL; - $sourcekeyfield = trim($domDirectory->getAttribute('source-key-field')); + $sourcekeyfield = trim((string) $domDirectory->getAttribute('source-key-field')); $curDir->sourceKeyField = strlen($sourcekeyfield) ? $sourcekeyfield : '@id'; - $sourcedisplayfield = trim($domDirectory->getAttribute('source-display-field')); + $sourcedisplayfield = trim((string) $domDirectory->getAttribute('source-display-field')); $curDir->sourceDisplayField = strlen($sourcedisplayfield) ? $sourcedisplayfield : '.'; - $sourceitem = trim($domDirectory->getAttribute('source-item')); + $sourceitem = trim((string) $domDirectory->getAttribute('source-item')); $curDir->sourceItem = strlen($sourceitem) ? $sourceitem : './*'; - $sourcedatafields = trim($domDirectory->getAttribute('source-data-fields')); + $sourcedatafields = trim((string) $domDirectory->getAttribute('source-data-fields')); $curDir->sourceDataFields = strlen($sourcedatafields) ? $sourcedatafields : '@*[local-name() != "id"]'; if ($curDir->load == 'automatic' && $curDir->schema == 'xml') { @@ -415,11 +408,9 @@ final class PXApplication } /** - * Load properties from database. - * - * @param PXDatabase $database - */ - public function loadProperties(PXDatabase $database) + * Load properties from database. + */ + public function loadProperties(PXDatabase $database) { $propertyList = PropertyLoader::getPropertyList($database); $this->properties->fromArray($propertyList); @@ -529,9 +520,6 @@ final class PXApplication $this->bindingsQueue->sort(); } - /** - * @param PXModuleDescription $module - */ public function setModules(PXModuleDescription $module) { $this->modules[$module->getName()] = $module; @@ -559,8 +547,8 @@ final class PXApplication $module = (new PXModuleDescription()) ->setName($lcname) - ->setDescription(isset($args['description']) ? $args['description'] : $ucfname) - ->setClass(isset($args['class']) ? $args['class'] : 'PXModule' . $ucfname); + ->setDescription($args['description'] ?? $ucfname) + ->setClass($args['class'] ?? 'PXModule' . $ucfname); $this->setModules($module); @@ -734,7 +722,7 @@ final class PXApplication */ public function __get($name) { - if (strtolower($name) === 'engine') { + if (strtolower((string) $name) === 'engine') { return PXRegistry::getEngine(); } @@ -754,7 +742,7 @@ final class PXApplication throw new Exception( sprintf( "Class: %s is not instanceof InitializableDescriptionInterface", - get_class($o) + $o::class ) ); } @@ -763,4 +751,20 @@ final class PXApplication } } } + + /** + * {@inheritDoc} + */ + public function setDataType(string $typeId, PXTypeDescription $type): void + { + $this->types[$typeId] = $type; + } + + /** + * {@inheritDoc} + */ + public function getDataType(string $typeId): ?PXTypeDescription + { + return $this->types[$typeId] ?? null; + } } diff --git a/lib/Config/bindingsQueue.class.inc b/lib/Config/bindingsQueue.class.inc index f89c9f73..9c3902ab 100644 --- a/lib/Config/bindingsQueue.class.inc +++ b/lib/Config/bindingsQueue.class.inc @@ -4,7 +4,7 @@ require_once PPLIBPATH . 'Config/Description/Bindings/classes.inc'; class PXBindingsQueue implements Iterator { private $pOrder, $rules; - private $finalize; + private bool $finalize; public function __construct() { @@ -17,7 +17,7 @@ class PXBindingsQueue implements Iterator { $rArea = PXRegistry::getRequest()->getArea(); - if (strlen($rArea) && sizeof(PXRegistry::getRequest()->getHostAndDir()) <= 1) { + if (strlen((string) $rArea) && sizeof(PXRegistry::getRequest()->getHostAndDir()) <= 1) { // FIXME // array_unshift($this->rules, new PXBindingDescription($this->app, array('type' => 'request', 'module' => $rArea), $this->pOrder++)); $this->__addRule('PXBindingDescription', ['type' => 'request', 'module' => $rArea]); @@ -40,7 +40,7 @@ class PXBindingsQueue implements Iterator ]; $cls = 'PXBindingDescription'; - $type = preg_replace('/[^0-9a-z]/' . REGEX_MOD, '', $type); + $type = preg_replace('/[^0-9a-z]/' . REGEX_MOD, '', (string) $type); if (class_exists($cls . $type)) { $cls .= $type; @@ -51,7 +51,7 @@ class PXBindingsQueue implements Iterator public function sort() { - uasort($this->rules, [$this, 'sortBindings']); + uasort($this->rules, $this->sortBindings(...)); } /* Iterator methods */ diff --git a/lib/Database/database.class.inc b/lib/Database/database.class.inc index 9c74ebbc..feefc0ca 100644 --- a/lib/Database/database.class.inc +++ b/lib/Database/database.class.inc @@ -114,14 +114,12 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface } /** - * Загружает справочники (directories) для одного объекта - * - * @param PXTypeDescription $format - * @param array|null $object - * @uses LoadDirectory - * - */ - public function LoadDirectoriesByType(PXTypeDescription $format, ?array $object = null): void + * Загружает справочники (directories) для одного объекта + * + * @uses LoadDirectory + * + */ + public function LoadDirectoriesByType(PXTypeDescription $format, ?array $object = null): void { foreach ($format->fields as $k => $v) { if ( @@ -135,11 +133,9 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface } /** - * Загружает справочники (directories) для таблиц (только для listed полей) - * @param PXTypeDescription $format - * @param array|null $objects - */ - public function loadDropdownValuesForListedFields(PXTypeDescription $format, ?array $objects = null): void + * Загружает справочники (directories) для таблиц (только для listed полей) + */ + public function loadDropdownValuesForListedFields(PXTypeDescription $format, ?array $objects = null): void { if (!$objects) { return; @@ -157,13 +153,12 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface } /** - * Загружает справочник (directories) в зависимости от типа - * - * @param PXDirectoryDescription $directory - * @uses LoadSQLDirectory, PXApplication::LoadXMLDirectory - * - */ - public function LoadDirectory(PXDirectoryDescription $directory, $object, $addDefault = true, $format = NULL) + * Загружает справочник (directories) в зависимости от типа + * + * @uses LoadSQLDirectory, PXApplication::LoadXMLDirectory + * + */ + public function LoadDirectory(PXDirectoryDescription $directory, $object, $addDefault = true, $format = NULL) { switch ($directory->schema) { case 'sql': @@ -177,34 +172,30 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface } /** - * Парсит WHERE в справочниках - * - * @param mixed $where - * @param mixed $object - * @param mixed $format - * @return string - * @uses _NormalizeObjectAttribute - * - * @todo docme подробнее - * - * @uses MapData - */ - public function ParseWhereTemplate($where, $object, $format) - { - if (strpos($where, 'THIS.') !== false) { + * Парсит WHERE в справочниках + * + * @return string + * @uses _NormalizeObjectAttribute + * @todo docme подробнее + * + * @uses MapData + */ + public function ParseWhereTemplate(mixed $where, mixed $object, mixed $format) + { + if (str_contains((string) $where, 'THIS.')) { if (is_array($object)) { - preg_match_all('/THIS\.([A-Z0-9\-_]+)/' . REGEX_MOD, $where, $backhref); + preg_match_all('/THIS\.([A-Z0-9\-_]+)/' . REGEX_MOD, (string) $where, $backhref); foreach ($backhref[1] as $b) { - if (array_key_exists(mb_strtolower($b), $object)) { + if (array_key_exists(mb_strtolower((string) $b), $object)) { if (is_null($format)) { - $replace = $this->MapData($object[strtolower($b)]); + $replace = $this->MapData($object[strtolower((string) $b)]); } else { $nullObj = null; - $replace = $this->MapData($this->_NormalizeObjectAttribute(strtolower($b), $object, $format->fields[strtolower($b)], $nullObj, $nullObj)); + $replace = $this->MapData($this->_NormalizeObjectAttribute(strtolower((string) $b), $object, $format->fields[strtolower((string) $b)], $nullObj, $nullObj)); } - $where = str_replace('THIS.' . $b, $replace, $where); + $where = str_replace('THIS.' . $b, $replace, (string) $where); } else { $where = 'FALSE'; } @@ -215,13 +206,13 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface } } - if (strpos($where, 'PROPERTY.') !== false) { - preg_match_all('/PROPERTY\.([A-Z0-9_]+)/' . REGEX_MOD, $where, $backhref); + if (str_contains((string) $where, 'PROPERTY.')) { + preg_match_all('/PROPERTY\.([A-Z0-9_]+)/' . REGEX_MOD, (string) $where, $backhref); foreach ($backhref[1] as $b) { $prop = $this->app->GetProperty($b); if (!is_null($prop)) { - $where = str_replace('PROPERTY.' . $b, $this->MapData($prop), $where); + $where = str_replace('PROPERTY.' . $b, $this->MapData($prop), (string) $where); } else { $where = 'FALSE'; } @@ -304,7 +295,7 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface } else { $selector = $this->_createSelector($format); $order = is_null($order) ? $format->order : $order; - $first_is_fun = preg_match('#^\s*\w+\s*\(#S' . REGEX_MOD, $order); + $first_is_fun = preg_match('#^\s*\w+\s*\(#S' . REGEX_MOD, (string) $order); $order_by = ($first_is_fun ? '' : $format->id . '.') . $order; } @@ -441,7 +432,7 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface $dontUseCache ); - if (count($data)) { + if (count((array) $data)) { return reset($data); } else { return []; @@ -530,7 +521,7 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface * @param Integer $mode request mode, default = {@link DB_SELECT_TABLE} * @return array */ - public function getObjectsByParent($format, $status, $parent, $mode = DB_SELECT_TABLE, $order = NULL) + public function getObjectsByParent($format, $status, mixed $parent, $mode = DB_SELECT_TABLE, $order = NULL) { return $this->getObjectsByParentLimited($format, $status, $parent, null, null, $mode, $order); } @@ -676,16 +667,11 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface protected function emptyResult($mode) { - switch ($mode) { - case DB_SELECT_TABLE: - $res = []; - break; - case DB_SELECT_COUNT: - $res = 0; - break; - default: - $res = null; - } + $res = match ($mode) { + DB_SELECT_TABLE => [], + DB_SELECT_COUNT => 0, + default => null, + }; return $res; } @@ -855,7 +841,7 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface */ public function AddContentObjects($format, $objectsArray) { - $audit = PXAuditLogger::getLogger($this->app, $this); + $audit = PXAuditLogger::getLogger(); if (count($objectsArray) == 0) { return; @@ -960,7 +946,7 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface public function cloneContentObject(&$format, $objectData, $status = false) { - $audit = PXAuditLogger::getLogger($this->app, $this); + $audit = PXAuditLogger::getLogger(); if (is_array($objectData) && isset($objectData['id']) && is_numeric($objectData['id']) && $objectData['id'] > 0) { $donorObject = $objectData; @@ -1136,9 +1122,7 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface $param = [ OBJ_FIELD_ID => $object[OBJ_FIELD_ID], 'format' => $format->id, - OBJ_FIELD_META => (isset($objectInDB[OBJ_FIELD_META])) - ? $objectInDB[OBJ_FIELD_META] - : [], + OBJ_FIELD_META => $objectInDB[OBJ_FIELD_META] ?? [], ]; $proceedFileResult = $v->storageType->proceedFile($v, $object, $param); @@ -1170,7 +1154,7 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface $this->transactionCommit(); $this->clearObjectTypeCache($format->id); - $object[OBJ_FIELD_ORDER] = isset($objectInDB[OBJ_FIELD_ORDER]) ? $objectInDB[OBJ_FIELD_ORDER] : null; + $object[OBJ_FIELD_ORDER] = $objectInDB[OBJ_FIELD_ORDER] ?? null; $object[OBJ_FIELD_META] = $sysMetaField; $object[OBJ_FIELD_UUID] = $objectInDB[OBJ_FIELD_UUID]; @@ -1363,7 +1347,7 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface */ public function _moveContentObject($format, $objectId, $direction, $steps = 1) { - $audit = PXAuditLogger::getLogger($this->app, $this); + $audit = PXAuditLogger::getLogger(); $objectInDB = $this->getObjectById($format, $objectId); if (!count($objectInDB)) $this->FatalError('Попытка модифицировать отсутствующий объект'); @@ -1374,7 +1358,7 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface $this->FatalError($errString); } - $swap = !!preg_match('#sys_order\s+DESC#i' . REGEX_MOD, $format->order); + $swap = !!preg_match('#sys_order\s+DESC#i' . REGEX_MOD, (string) $format->order); $down = ($swap xor ($direction == 'down')); $where = []; @@ -1461,7 +1445,7 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface */ public function _NormalizeObjectPathName($format, $object, &$pathnames) { - $url = mb_strtolower(_TranslitModern($object['pathname'])); + $url = mb_strtolower((string) _TranslitModern($object['pathname'])); // part 1: make url if doesn't set if (!$url) { @@ -1475,16 +1459,16 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface } } - if (!$url) $url = mb_strtolower(_TranslitModern(mb_substr(preg_replace('/\&.+?\;/' . REGEX_MOD, '', $object['title']), 0, 32))); - if (!$url) $url = sprintf('%08d', rand(0, 100000000)); + if (!$url) $url = mb_strtolower((string) _TranslitModern(mb_substr(preg_replace('/\&.+?\;/' . REGEX_MOD, '', (string) $object['title']), 0, 32))); + if (!$url) $url = sprintf('%08d', random_int(0, 100_000_000)); // part 2: triming bad chars - very readable... gj. $url = trim($url); for ($i = 0; $i < strlen($url); $i++) { - $o = ord($url{$i}); + $o = ord($url[$i]); if ($o != 45 && $o != 46 && ($o < 48 || $o > 57) && ($o < 65 || $o > 90) && $o != 95 && ($o < 97 || $o > 122) && ($o != 58)) { - $url{$i} = '_'; + $url[$i] = '_'; } } @@ -1525,15 +1509,15 @@ class PXDatabase extends DatabaseAdapter implements DatabaseInterface // FIXME: very ugly code. No need mb_* functions here, working under _Translit'ed string if ($reiteration > 0) { - if (strpos($url, '.') !== false) { - if (is_numeric($url{strrpos($url, '.') - 1})) { - $url = substr($url, 0, strrpos($url, '.') - 1) . ($url{strrpos($url, '.') - 1} + 1) . substr($url, strrpos($url, '.')); + if (str_contains($url, '.')) { + if (is_numeric($url[strrpos($url, '.') - 1])) { + $url = substr($url, 0, strrpos($url, '.') - 1) . ($url[strrpos($url, '.') - 1] + 1) . substr($url, strrpos($url, '.')); } else { $url = substr($url, 0, strrpos($url, '.')) . '1' . substr($url, strrpos($url, '.')); } } else { - if (is_numeric($url{strlen($url) - 1})) { - $url = substr($url, 0, strlen($url) - 1) . ($url{strlen($url) - 1} + 1); + if (is_numeric($url[strlen($url) - 1])) { + $url = substr($url, 0, strlen($url) - 1) . ($url[strlen($url) - 1] + 1); } else { $url = $url . '1'; } @@ -1580,7 +1564,7 @@ SQL; * @todo docme (check params documentation) * */ - public function getLinksLimited($reference, $format, $id, $limit, $offset) + public function getLinksLimited(mixed $reference, $format, mixed $id, $limit, $offset) { $linkedFormat = $reference->getOther($format); @@ -1679,7 +1663,7 @@ SQL; * @todo docme (check params documentation) * */ - public function GetLinks($reference, $format, $id) + public function GetLinks(mixed $reference, $format, mixed $id) { return $this->GetLinksLimited($reference, $format, $id, NULL, NULL); } @@ -1694,7 +1678,7 @@ SQL; * @todo docme (check params documentation) * */ - public function GetLinksCount($reference, $format, $id) + public function GetLinksCount(mixed $reference, $format, mixed $id) { $linkedFormat = $reference->getOther($format); $query = ''; @@ -1795,7 +1779,7 @@ SQL; * @todo docme (check params documentation) * */ - public function ModifyLinks($reference, $format, $id, $links, $deleteAll = true) + public function ModifyLinks(mixed $reference, $format, mixed $id, mixed $links, $deleteAll = true) { if (!is_array($links) || !sizeof($links)) { return; @@ -1942,7 +1926,7 @@ SQL; if ($isMetaPresent) { $sysMeta = ($table[$k][OBJ_FIELD_META] != null) - ? json_decode($table[$k][OBJ_FIELD_META], true) + ? json_decode((string) $table[$k][OBJ_FIELD_META], true) : []; $sysMeta = is_array($sysMeta) @@ -1961,7 +1945,7 @@ SQL; } } - if (count($format->childs)) { + if (is_countable($format->childs) ? count($format->childs) : 0) { if (!empty($table[$k][OBJ_FIELD_CHILDREN])) { // TODO: replace with json_decode and provide migration script $table[$k][OBJ_FIELD_CHILDREN] = unserialize($table[$k][OBJ_FIELD_CHILDREN]); @@ -2031,7 +2015,7 @@ SQL; public function buildTree() { $args = func_get_args(); - $treeKlass = new ReflectionClass('PP\Lib\Datastruct\Tree'); + $treeKlass = new ReflectionClass(\PP\Lib\Datastruct\Tree::class); return $treeKlass->newInstanceArgs($args); } @@ -2063,7 +2047,7 @@ SQL; return; } - $audit = PXAuditLogger::getLogger($this->app, $this); + $audit = PXAuditLogger::getLogger(); $permitCheck = null; $parentObject = null; @@ -2078,7 +2062,7 @@ SQL; $parentObject = $this->getObjectById($this->types[$format->parent], $object['parent']); } - if ($permitCheck === false || !count($parentObject)) { + if ($permitCheck === false || !count((array) $parentObject)) { $errString = 'Попытка переместить объект в несуществующий раздел'; $audit->error($errString, $format->id . '/' . $object['id']); $this->FatalError($errString); @@ -2117,22 +2101,19 @@ SQL; } /** - * Метод генерирует массив строк sql where conditions из ассоциативного массива field => value - * @param array $fields - * @param string $formatId - * @return array - * @example ['!f1' => 'test', '!f2' => true, '!f3' => null] вернет ["f1 <> 'test'", 'f2 <> TRUE', 'f3 IS NOT NULL'] - * - * @example ['f1' => 'test', 'f2' => true, 'f3' => null] вернет ["f1 = 'test'", 'f2 = TRUE', 'f3 IS NULL'] - * Если в ключ добавить `!` первым символом - условие будет выстроено от обратного - */ - public function buildWhereFromArray(array $fields, string $formatId = ''): array + * Метод генерирует массив строк sql where conditions из ассоциативного массива field => value + * @example ['!f1' => 'test', '!f2' => true, '!f3' => null] вернет ["f1 <> 'test'", 'f2 <> TRUE', 'f3 IS NOT NULL'] + * + * @example ['f1' => 'test', 'f2' => true, 'f3' => null] вернет ["f1 = 'test'", 'f2 = TRUE', 'f3 IS NULL'] + * Если в ключ добавить `!` первым символом - условие будет выстроено от обратного + */ + public function buildWhereFromArray(array $fields, string $formatId = ''): array { $whereClause = []; foreach ($fields as $k => $v) { $not = false; - if (strpos($k, '!') === 0) { + if (str_starts_with($k, '!')) { $not = true; $k = str_replace('!', '', $k); } @@ -2144,7 +2125,7 @@ SQL; case is_array($v): empty($v) && $this->FatalError("Array value for db#getObjectsByField* can't be empty!"); - $val = sprintf("%s IN('%s')", $not ? ' NOT' : '', join("','", array_map([$this, 'EscapeString'], $v))); + $val = sprintf("%s IN('%s')", $not ? ' NOT' : '', join("','", array_map($this->EscapeString(...), $v))); break; case is_bool($v): diff --git a/lib/Debug/ErrorReporter/abstract.class.inc b/lib/Debug/ErrorReporter/abstract.class.inc index bc2daaf8..7e189581 100644 --- a/lib/Debug/ErrorReporter/abstract.class.inc +++ b/lib/Debug/ErrorReporter/abstract.class.inc @@ -56,7 +56,7 @@ class PXErrorReporter } public static - function fatalError($message) + function fatalError($message): void { $reporter =& PXErrorReporter::getInstance(); @@ -107,7 +107,7 @@ class PXErrorReporter NL_FATAL_ERROR => 'Fatal Error', ]; - return isset($errorTypes[$code]) ? $errorTypes[$code] : 'Unknown error type ' . $code; + return $errorTypes[$code] ?? 'Unknown error type ' . $code; } public function _addError($code, $message, $file, $line, $traceFromLine = 2) diff --git a/lib/Debug/ErrorReporter/html.class.inc b/lib/Debug/ErrorReporter/html.class.inc index 96dff1a2..43b752fa 100644 --- a/lib/Debug/ErrorReporter/html.class.inc +++ b/lib/Debug/ErrorReporter/html.class.inc @@ -8,7 +8,7 @@ class PXErrorReporterHTML extends PXErrorReporter public function __construct() { - $this->computer = @rtrim(`hostname`); + $this->computer = @rtrim((string) `hostname`); $this->toEmail = !empty($_SERVER['SERVER_ADMIN']) ? $_SERVER['SERVER_ADMIN'] : 'support@dalee.ru'; $this->fromEmail = 'robot@' . $this->computer; @@ -23,7 +23,7 @@ class PXErrorReporterHTML extends PXErrorReporter { $response = Response::getInstance(); $host = quot(PXRequest::GetHttpHost()); - $uri = quot(urldecode(PXRequest::GetRequestUri())); + $uri = quot(urldecode((string) PXRequest::GetRequestUri())); $response->unavailable(); $response->send(<< @@ -58,7 +58,7 @@ HTML $app = PXRegistry::getApp(); // APP may not be initialized yet and throws exception $errorRepDir = $app->getProperty('PP_ERROR_REPORTER_ANTIFLOOD_DIR', 'error_reporter'); $cacheTime = (int)$app->getProperty('PP_ERROR_REPORTER_ANTIFLOOD_LAG', 30); - } catch (Exception $e) { + } catch (Exception) { $errorRepDir = 'error_reporter'; $cacheTime = 30; } @@ -86,26 +86,13 @@ HTML @$errorTypes[$e['type']]++; } - uksort($errorTypes, function ($a, $b) { - switch (true) { - case ($a == $b): - return 0; - - case ($a == 'Fatal Error'): - return 1; - - case ($b == 'Fatal Error'): - return -1; - - case ($a == 'Notice'): - return -1; - - case ($b == 'Notice'): - return 1; - - default: - return 1; - } + uksort($errorTypes, fn($a, $b) => match (true) { + $a == $b => 0, + $a == 'Fatal Error' => 1, + $b == 'Fatal Error' => -1, + $a == 'Notice' => -1, + $b == 'Notice' => 1, + default => 1, }); $subject = []; @@ -139,10 +126,10 @@ HTML $errorsListHTML = []; foreach ($this->errors as $errPos => $error) { - $cls = strtolower(str_replace(' ', '', $error['type'])); + $cls = strtolower(str_replace(' ', '', (string) $error['type'])); $this->attachError($error, $errPos); - $message = htmlspecialchars($error['message'], ENT_QUOTES | ENT_DISALLOWED | ENT_SUBSTITUTE, 'UTF-8'); + $message = htmlspecialchars((string) $error['message'], ENT_QUOTES | ENT_DISALLOWED | ENT_SUBSTITUTE, 'UTF-8'); $errorsListHTML[] = << {$error['type']}: @@ -181,9 +168,9 @@ HTML; public function environmentsUris() { - $requestUri = PXRequest::GetHttpProto() . '://' . quot(PXRequest::GetHttpHost()) . quot(urldecode(PXRequest::GetRequestUri())); + $requestUri = PXRequest::GetHttpProto() . '://' . quot(PXRequest::GetHttpHost()) . quot(urldecode((string) PXRequest::GetRequestUri())); $remoteAddr = PXRequest::GetRemoteAddr(); - $httpReferer = quot(urldecode(PXRequest::GetHttpReferer())); + $httpReferer = quot(urldecode((string) PXRequest::GetHttpReferer())); return <<RequestUri: @@ -200,7 +187,7 @@ HTML; static $xslt; if (is_null($xslt)) { - $xslt = new PXLibXSLT(dirname(__FILE__) . '/style.xsl'); + $xslt = new PXLibXSLT(__DIR__ . '/style.xsl'); } return $xslt->transform($xml, false); @@ -255,7 +242,7 @@ HTML; { $traceXML = $this->traceToXML($error['trace']); - $message = htmlspecialchars($error['message'], ENT_XML1 | ENT_QUOTES | ENT_DISALLOWED | ENT_SUBSTITUTE, 'UTF-8'); + $message = htmlspecialchars((string) $error['message'], ENT_XML1 | ENT_QUOTES | ENT_DISALLOWED | ENT_SUBSTITUTE, 'UTF-8'); return << @@ -282,9 +269,7 @@ XML; '', array_map( - function ($a) { - return "\n\t\t\t\t<{$a['type']}>{$a['value']}"; - }, + fn($a) => "\n\t\t\t\t<{$a['type']}>{$a['value']}", $error['args'] ) ); diff --git a/lib/Debug/functions.inc b/lib/Debug/functions.inc index 821c8b82..89dddaed 100644 --- a/lib/Debug/functions.inc +++ b/lib/Debug/functions.inc @@ -8,6 +8,6 @@ require_once PPLIBPATH . 'Debug/ErrorReporter/abstract.class.inc'; require_once PPLIBPATH . 'Debug/sbin.inc'; function getMicroTime() { - list($usec, $sec) = explode(" ", microtime()); + [$usec, $sec] = explode(" ", microtime()); return ((float)$usec + (float)$sec); } diff --git a/lib/Debug/trace.inc b/lib/Debug/trace.inc index b2a11126..03c36d53 100644 --- a/lib/Debug/trace.inc +++ b/lib/Debug/trace.inc @@ -26,35 +26,15 @@ function PXTrace() { foreach ($call['args'] as $k=>$v) { $type = gettype($v); - switch($type) { - case 'resource': - $value = get_resource_type($v); - break; - - case 'array': - $value = '['.sizeof($v).']'; - break; - - case 'object': - $value = get_class($v); - break; - - case 'boolean': - $value = $v ? 'true' : 'false'; - break; - - case 'string': - $value = "'$v'"; - break; - - case 'NULL': - $value = 'NULL'; - break; - - default: - $value = (string)$v; - break; - } + $value = match ($type) { + 'resource' => get_resource_type($v), + 'array' => '['.sizeof($v).']', + 'object' => $v::class, + 'boolean' => $v ? 'true' : 'false', + 'string' => "'$v'", + 'NULL' => 'NULL', + default => (string)$v, + }; $e['args'][$k] = ['type'=>$type, 'value'=>$value]; } @@ -67,4 +47,3 @@ function PXTrace() { return $trace; } -?> diff --git a/lib/DisplayType/Dropdown/dropdown.class.inc b/lib/DisplayType/Dropdown/dropdown.class.inc index e6f3815d..39651def 100644 --- a/lib/DisplayType/Dropdown/dropdown.class.inc +++ b/lib/DisplayType/Dropdown/dropdown.class.inc @@ -20,7 +20,7 @@ class PXDisplayTypeDropdown extends PXDisplayType public function buildInput($field, $object, $param = NULL) { - $value = isset($object[$field->name]) ? $object[$field->name] : NULL; + $value = $object[$field->name] ?? NULL; return NLAbstractHTMLForm::BuildDropDown($field->name, $value, $field->values->GetList()); } @@ -31,7 +31,7 @@ class PXDisplayTypeDropdown extends PXDisplayType } $displayField = $field->values->displayField; $value = $object[$field->name]; - return isset($field->values->values[$value][$displayField]) ? $field->values->values[$value][$displayField] : ''; + return $field->values->values[$value][$displayField] ?? ''; } public function buildTreeHover($field, $object, $param = NULL) @@ -44,7 +44,7 @@ class PXDisplayTypeDropdown extends PXDisplayType { $format = ''; $parentFormat = ''; - if (count($param) && isset($param['datatype']) && is_object($param['datatype'])) { + if ((is_countable($param) ? count($param) : 0) && isset($param['datatype']) && is_object($param['datatype'])) { $format = $param['datatype']->id; $parentFormat = $param['datatype']->parent; } diff --git a/lib/DisplayType/Dropdown/multiDropdown.class.inc b/lib/DisplayType/Dropdown/multiDropdown.class.inc index 3a263606..5b51a273 100644 --- a/lib/DisplayType/Dropdown/multiDropdown.class.inc +++ b/lib/DisplayType/Dropdown/multiDropdown.class.inc @@ -8,9 +8,7 @@ class PXDisplayTypeMultiDropDown extends PXDisplayTypeDropdown ? $object[$field->name] : NULL; - $size = isset($field->displayTypeArgs[0]) - ? $field->displayTypeArgs[0] - : NULL; + $size = $field->displayTypeArgs[0] ?? NULL; $value = array_key_exists(0, (array)$value) && $value[0] == "NULL" ? "" : $value; @@ -28,7 +26,7 @@ class PXDisplayTypeMultiDropDown extends PXDisplayTypeDropdown $value = []; if (is_array($object[$field->name])) { - $value = array_filter($object[$field->name], [$this, "_filterNULL"]); + $value = array_filter($object[$field->name], $this->_filterNULL(...)); } $values = $field->values->values; diff --git a/lib/DisplayType/Dropdown/parentDropdown.class.inc b/lib/DisplayType/Dropdown/parentDropdown.class.inc index e416ce9f..d7854043 100644 --- a/lib/DisplayType/Dropdown/parentDropdown.class.inc +++ b/lib/DisplayType/Dropdown/parentDropdown.class.inc @@ -10,7 +10,7 @@ class PXDisplayTypeParentdropdown extends PXDisplayTypeDropdown { public function buildInput($field, $object, $param = NULL) { - $value = isset($object[$field->name]) ? $object[$field->name] : NULL; + $value = $object[$field->name] ?? NULL; [$format, $parentFormat] = parent::getFormats($param); $tree = parent::getParentTree($object, $parentFormat); @@ -21,7 +21,10 @@ class PXDisplayTypeParentdropdown extends PXDisplayTypeDropdown } foreach ($parents as $k => $v) { - @$allowed =& $tree->leafs[$k]->content['allowed']; + + if (isset($tree->leafs[$k])) { + @$allowed =& $tree->leafs[$k]->content['allowed']; + } if (isset($allowed[$format]) || $format == $parentFormat) { $parents[$k] = ['title' => $v]; diff --git a/lib/DisplayType/Dropdown/selfParentDropdown.class.inc b/lib/DisplayType/Dropdown/selfParentDropdown.class.inc index 0996ed66..166bfd9c 100644 --- a/lib/DisplayType/Dropdown/selfParentDropdown.class.inc +++ b/lib/DisplayType/Dropdown/selfParentDropdown.class.inc @@ -10,7 +10,7 @@ class PXDisplayTypeSelfparentdropdown extends PXDisplayTypeParentdropdown { public function buildInput($field, $object, $param = NULL) { - $value = isset($object[$field->name]) ? $object[$field->name] : NULL; + $value = $object[$field->name] ?? NULL; $selfTree = parent::getSelfParentTree($object, $param); $selfTree = $selfTree->getPlainTree(getFromArray($object, 'id')); if (isset($param['root_title'])) { diff --git a/lib/DisplayType/Dropdown/treeDropdown.class.inc b/lib/DisplayType/Dropdown/treeDropdown.class.inc index 0220830a..1e637fbb 100644 --- a/lib/DisplayType/Dropdown/treeDropdown.class.inc +++ b/lib/DisplayType/Dropdown/treeDropdown.class.inc @@ -11,7 +11,7 @@ class PXDisplayTypeTreedropdown extends PXDisplayTypeDropdown { public function buildInput($field, $object, $param = NULL) { - $value = isset($object[$field->name]) ? $object[$field->name] : NULL; + $value = $object[$field->name] ?? NULL; return NLAbstractHTMLForm::BuildDropDown($field->name, $value, $this->getListFromField($field)); } diff --git a/lib/DisplayType/File/file.class.inc b/lib/DisplayType/File/file.class.inc index 1fc5ea2b..a7012b54 100644 --- a/lib/DisplayType/File/file.class.inc +++ b/lib/DisplayType/File/file.class.inc @@ -33,8 +33,6 @@ class PXDisplayTypeFile extends PXDisplayType return ''; } - return '' . $fileArray['filename'] . ' ' . number_format($fileArray['size'] / 1024, 0, ',', ' ') . 'Кб
'; + return '' . $fileArray['filename'] . ' ' . number_format($fileArray['size'] / 1024, 0, ',', ' ') . 'Кб
'; } } - -?> diff --git a/lib/DisplayType/File/filesArray.class.inc b/lib/DisplayType/File/filesArray.class.inc index 83aff5fc..e7665705 100644 --- a/lib/DisplayType/File/filesArray.class.inc +++ b/lib/DisplayType/File/filesArray.class.inc @@ -9,7 +9,7 @@ class PXDisplayTypeFilesarray extends PXDisplayTypeImagesarray { public function buildArrayItem($name, $value) { - if (!mb_strlen($value['entry'])) { + if (!mb_strlen((string) $value['entry'])) { $value = [ 'filename' => null, 'size' => null, @@ -39,4 +39,3 @@ class PXDisplayTypeFilesarray extends PXDisplayTypeImagesarray return $html; } } -?> diff --git a/lib/DisplayType/File/image.class.inc b/lib/DisplayType/File/image.class.inc index 3420f5a7..a7a09f56 100644 --- a/lib/DisplayType/File/image.class.inc +++ b/lib/DisplayType/File/image.class.inc @@ -34,8 +34,8 @@ class PXDisplayTypeImage extends PXDisplayType $html = '
'; if (isset($value['path'])) { - $width = isset($value['width']) ? $value['width'] : $maxWidth; - $height = isset($value['height']) ? $value['height'] : $maxHeight; + $width = $value['width'] ?? $maxWidth; + $height = $value['height'] ?? $maxHeight; [$width, $height] = $this->getDisplaySizes($width, $height, $maxWidth, $maxHeight); $html .= ''; } @@ -100,5 +100,3 @@ class PXDisplayTypeImage extends PXDisplayType return [(int)$w, (int)$h]; } } - -?> diff --git a/lib/DisplayType/File/imagesArray.class.inc b/lib/DisplayType/File/imagesArray.class.inc index 82e9b23f..45646a26 100644 --- a/lib/DisplayType/File/imagesArray.class.inc +++ b/lib/DisplayType/File/imagesArray.class.inc @@ -10,7 +10,7 @@ class PXDisplayTypeImagesarray extends PXDisplayTypeImage { public function buildInput($field, $object, $param = NULL) { - $object[$field->name] = isset($object[$field->name]) ? $object[$field->name] : []; + $object[$field->name] ??= []; $items = array_merge( $object[$field->name], @@ -66,5 +66,3 @@ class PXDisplayTypeImagesarray extends PXDisplayTypeImage return $html; } } - -?> diff --git a/lib/DisplayType/Time/date.class.inc b/lib/DisplayType/Time/date.class.inc index 5b80a558..31ebcb7f 100644 --- a/lib/DisplayType/Time/date.class.inc +++ b/lib/DisplayType/Time/date.class.inc @@ -17,7 +17,6 @@ class PXDisplayTypeDate extends PXDisplayTypeTimestamp public function buildCell($field, $object, $param = NULL) { - return mb_substr($object[$field->name], 0, 10); + return mb_substr((string) $object[$field->name], 0, 10); } } -?> diff --git a/lib/DisplayType/Time/time.class.inc b/lib/DisplayType/Time/time.class.inc index 6869997b..4b019199 100644 --- a/lib/DisplayType/Time/time.class.inc +++ b/lib/DisplayType/Time/time.class.inc @@ -18,7 +18,6 @@ class PXDisplayTypeTime extends PXDisplayTypeTimestamp public function buildCell($field, $object, $param = NULL) { - return mb_substr($object[$field->name], 11); + return mb_substr((string) $object[$field->name], 11); } } -?> diff --git a/lib/DisplayType/abstract.class.inc b/lib/DisplayType/abstract.class.inc index 33918dec..eea8df85 100644 --- a/lib/DisplayType/abstract.class.inc +++ b/lib/DisplayType/abstract.class.inc @@ -15,12 +15,10 @@ use PP\Lib\Engine\EngineInterface; class PXDisplayType { public $display; - public $name; - public function __construct(&$display, $name) + public function __construct(&$display, public $name) { $this->display = &$display; - $this->name = $name; } /** @@ -188,18 +186,18 @@ class PXDisplayType if (isset($app->directory[$field->source])) { $directory = $app->directory[$field->source]; - $dirObject = isset($directory->values[$object[$field->name]]) ? $directory->values[$object[$field->name]] : null; + $dirObject = $directory->values[$object[$field->name]] ?? null; - $temp = isset($dirObject[$directory->displayField]) ? $dirObject[$directory->displayField] : ''; + $temp = $dirObject[$directory->displayField] ?? ''; } - return mb_strlen($temp) ? $temp : $value; + return mb_strlen((string) $temp) ? $temp : $value; } protected static function parseConfigArguments($attrNode, $fieldNode) { - $args = explode("|", pp_simplexml_decode_string($attrNode->value)); + $args = explode("|", (string) pp_simplexml_decode_string($attrNode->value)); $name = $args[0]; array_shift($args); @@ -213,14 +211,14 @@ class PXDisplayType $baseClass = 'PXDisplayType'; $autoload = $type->app->engine->engineClass() != EngineInterface::USER_ENGINE_ID; - $inputClass = $baseClass . mb_ucfirst(mb_strtolower($name)); + $inputClass = $baseClass . mb_ucfirst(mb_strtolower((string) $name)); return class_exists($inputClass, $autoload) ? $inputClass : $baseClass; } public static function parseConfig($attrNode, $fieldNode, $type) { - $name = preg_replace('/\|.+$/' . REGEX_MOD, '', $attrNode->value); + $name = preg_replace('/\|.+$/' . REGEX_MOD, '', (string) $attrNode->value); $cls = self::getClassByName($name, $type); $args = call_user_func([$cls, 'parseConfigArguments'], $attrNode, $fieldNode); diff --git a/lib/DisplayType/checkbox.class.inc b/lib/DisplayType/checkbox.class.inc index d7c024ab..d690c7ce 100644 --- a/lib/DisplayType/checkbox.class.inc +++ b/lib/DisplayType/checkbox.class.inc @@ -10,7 +10,7 @@ class PXDisplayTypeCheckbox extends PXDisplayType public function buildInput($field, $object, $param = NULL) { $value = isset($object[$field->name]) && $object[$field->name]; - return NLAbstractHTMLForm::BuildInputCheckBox($field->name, $value, null, isset($param['id']) ? $param['id'] : null); + return NLAbstractHTMLForm::BuildInputCheckBox($field->name, $value, null, $param['id'] ?? null); } public function buildCell($field, $object, $param = NULL) @@ -24,4 +24,3 @@ class PXDisplayTypeCheckbox extends PXDisplayType return $param['request']->postData->_GetCheckBoxVar($field->name); } } -?> diff --git a/lib/DisplayType/color.class.inc b/lib/DisplayType/color.class.inc index e4b2f37c..8034c0df 100644 --- a/lib/DisplayType/color.class.inc +++ b/lib/DisplayType/color.class.inc @@ -9,14 +9,14 @@ class PXDisplayTypeColor extends PXDisplayType { public function buildInput($field, $object, $param = NULL) { - $value = isset($object[$field->name]) ? $object[$field->name] : NULL; + $value = $object[$field->name] ?? NULL; return NLAbstractHTMLForm::BuildColor($field->name, $value); } public function buildCell($field, $object, $param = NULL) { $value = $object[$field->name]; - return ctype_alnum($value) && (mb_strlen($value) == 3 || mb_strlen($value) == 6) ? '
' : ''; + return ctype_alnum((string) $value) && (mb_strlen((string) $value) == 3 || mb_strlen((string) $value) == 6) ? '
' : ''; } } diff --git a/lib/DisplayType/ipAddr.class.inc b/lib/DisplayType/ipAddr.class.inc index 089ae07f..51de24db 100644 --- a/lib/DisplayType/ipAddr.class.inc +++ b/lib/DisplayType/ipAddr.class.inc @@ -10,14 +10,14 @@ class PXDisplayTypeIpaddr extends PXDisplayType public function buildInput($field, $object, $param = NULL) { $k = $field->name; - $value = explode(".", long2ip(isset($object[$k]) ? $object[$k] : 0)); + $value = explode(".", long2ip($object[$k] ?? 0)); return NLAbstractHTMLForm::BuildIPAddr($k, $value); } public function buildCell($field, $object, $param = NULL) { - return long2ip(isset($object[$field->name]) ? $object[$field->name] : 0); + return long2ip($object[$field->name] ?? 0); } public function getFromRequest($field, $object, $param = NULL) diff --git a/lib/DisplayType/jobResult.class.inc b/lib/DisplayType/jobResult.class.inc index b8043f57..9e16d5ed 100644 --- a/lib/DisplayType/jobResult.class.inc +++ b/lib/DisplayType/jobResult.class.inc @@ -7,10 +7,10 @@ class PXDisplayTypeJobResult extends PXDisplayType { public function buildInput($field, $object, $param = null) { $html = ''; - $value = isset($object[$field->name]) ? $object[$field->name] : []; + $value = $object[$field->name] ?? []; - $keys = explode('-', $field->displayTypeArgs[0]); - $titles = explode('-', $field->displayTypeArgs[1]); + $keys = explode('-', (string) $field->displayTypeArgs[0]); + $titles = explode('-', (string) $field->displayTypeArgs[1]); foreach ($keys as $idx => $key) { $title = $titles[$idx]; diff --git a/lib/DisplayType/password.class.inc b/lib/DisplayType/password.class.inc index 14373e02..0258acc0 100644 --- a/lib/DisplayType/password.class.inc +++ b/lib/DisplayType/password.class.inc @@ -9,14 +9,14 @@ class PXDisplayTypePassword extends PXDisplayType { public function buildInput($field, $object, $param = NULL) { - $value = isset($object[$field->name]) && mb_strlen($object[$field->name]) ? $object[$field->name] : NULL; + $value = isset($object[$field->name]) && mb_strlen((string) $object[$field->name]) ? $object[$field->name] : NULL; return NLAbstractHTMLForm::BuildPassword($field->name, $value); } public function buildCell($field, $object, $param = NULL) { $ret = ''; - if (mb_strlen($object[$field->name]) > 0) { + if (mb_strlen((string) $object[$field->name]) > 0) { $obj = new PXDisplayTypeCheckbox($this->display, $this->name); /* E_STRICT remove */ $ret = $obj->buildCell($field, [$field->name => true], $param); } @@ -27,7 +27,7 @@ class PXDisplayTypePassword extends PXDisplayType { $tmp = $param['request']->postData->_GetArrayVar($field->name); - $value = (isset($tmp['type']) && isset($tmp['retype']) && mb_strlen($tmp['type']) && ($tmp['type'] === $tmp['retype'])) ? trim($tmp['type']) : null; + $value = (isset($tmp['type']) && isset($tmp['retype']) && mb_strlen((string) $tmp['type']) && ($tmp['type'] === $tmp['retype'])) ? trim((string) $tmp['type']) : null; if (mb_strlen($value) && !is_null($value)) { $authDriver = PXRegistry::getUser() @@ -49,7 +49,7 @@ class PXDisplayTypePassword extends PXDisplayType public function preModifyObject($field, &$object, $param = NULL) { - if (!mb_strlen($object[$field->name])) { + if (!mb_strlen((string) $object[$field->name])) { return true; } diff --git a/lib/DisplayType/radiolist.class.inc b/lib/DisplayType/radiolist.class.inc index 4fe1bb6f..8269f878 100644 --- a/lib/DisplayType/radiolist.class.inc +++ b/lib/DisplayType/radiolist.class.inc @@ -3,7 +3,7 @@ class PXDisplayTypeRadioList extends PXDisplayTypeDropDown { public function buildInput($field, $object, $param = null) { $value = !empty($object[$field->name]) ? $object[$field->name] : 0; $values = $field->values->getList(); - if(!count($values)){ + if(!(is_countable($values) ? count($values) : 0)){ return; } @@ -21,4 +21,3 @@ class PXDisplayTypeRadioList extends PXDisplayTypeDropDown { return $selector->html(); } } -?> diff --git a/lib/DisplayType/richedit.class.inc b/lib/DisplayType/richedit.class.inc index 0afa8645..a703f482 100644 --- a/lib/DisplayType/richedit.class.inc +++ b/lib/DisplayType/richedit.class.inc @@ -14,7 +14,7 @@ class PXDisplayTypeRichedit extends PXDisplayTypeText { public function buildInput($field, $object, $param = NULL) { - $value = isset($object[$field->name]) ? $object[$field->name] : NULL; + $value = $object[$field->name] ?? NULL; $req = PXRegistry::getRequest(); $layout = PXRegistry::getLayout(); $redactor = $req->getCookieVar('richedit'); diff --git a/lib/DisplayType/static.class.inc b/lib/DisplayType/static.class.inc index 4264cb18..b95b7185 100644 --- a/lib/DisplayType/static.class.inc +++ b/lib/DisplayType/static.class.inc @@ -13,14 +13,14 @@ class PXDisplayTypeStatic extends PXDisplayType $value = isset($object[$k]) ? (is_array($object[$k]) ? print_r($object[$k], true) : $object[$k]) : NULL; $values = isset($field->values) ? $field->values->GetList() : []; - return NLAbstractHTMLForm::BuildHidden($field->name, $value) . htmlspecialchars(isset($values[$value]) ? $values[$value] : $value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); + return NLAbstractHTMLForm::BuildHidden($field->name, $value) . htmlspecialchars($values[$value] ?? $value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); } public function buildCell($field, $object, $param = NULL) { $value = $object[$field->name]; - return htmlspecialchars($this->getSubDirectoryIfExists($field, $object, $value), ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); + return htmlspecialchars((string) $this->getSubDirectoryIfExists($field, $object, $value), ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); } public function preModifyObject($field, &$object, $param = NULL) @@ -28,4 +28,3 @@ class PXDisplayTypeStatic extends PXDisplayType return (bool)$param['skipHidden']; } } -?> diff --git a/lib/DisplayType/staticordrop.class.inc b/lib/DisplayType/staticordrop.class.inc index f5a2d595..60839f05 100644 --- a/lib/DisplayType/staticordrop.class.inc +++ b/lib/DisplayType/staticordrop.class.inc @@ -10,12 +10,12 @@ class PXDisplayTypeStaticOrDrop extends PXDisplayType public function buildInput($field, $object, $param = NULL) { - $value = isset($object[$field->name]) ? $object[$field->name] : NULL; + $value = $object[$field->name] ?? NULL; $values = isset($field->values) ? $field->values->GetList() : []; $fvalues = $field->values->values; if (!empty($fvalues[$value]['static'])) { - return NLAbstractHTMLForm::BuildHidden($field->name, $value) . htmlspecialchars(isset($values[$value]) ? $values[$value] : $value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); + return NLAbstractHTMLForm::BuildHidden($field->name, $value) . htmlspecialchars($values[$value] ?? $value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); } else { foreach ($fvalues as $k => $fval) { if (isset($fval['static'])) { diff --git a/lib/DisplayType/table.class.inc b/lib/DisplayType/table.class.inc index d6e41e35..b4b15b4c 100644 --- a/lib/DisplayType/table.class.inc +++ b/lib/DisplayType/table.class.inc @@ -14,12 +14,12 @@ class PXDisplayTypeTable extends PXDisplayType { public function buildInput($field, $object, $param = NULL) { - $value = isset($object[$field->name]) ? $object[$field->name] : []; - $cols = isset($field->displayTypeArgs[0]) ? $field->displayTypeArgs[0] : '1'; - $height = isset($field->displayTypeArgs[1]) ? $field->displayTypeArgs[1] : 0; - $header = isset($field->displayTypeArgs[2]) ? explode(isset($field->displayTypeArgs[3]) ? $field->displayTypeArgs[3] : '-', $field->displayTypeArgs[2]) : []; + $value = $object[$field->name] ?? []; + $cols = $field->displayTypeArgs[0] ?? '1'; + $height = $field->displayTypeArgs[1] ?? 0; + $header = isset($field->displayTypeArgs[2]) ? explode($field->displayTypeArgs[3] ?? '-', (string) $field->displayTypeArgs[2]) : []; - return NLAbstractHTMLForm::BuildTableByCols($field->name, $value, explode("-", $cols), $height, $header); + return NLAbstractHTMLForm::BuildTableByCols($field->name, $value, explode("-", (string) $cols), $height, $header); } public function buildCell($field, $object, $param = NULL) @@ -29,7 +29,7 @@ class PXDisplayTypeTable extends PXDisplayType $html = ''; $html_len = 0; - for ($i = 0, $l = count($value); $i < $l && $html_len < 30; $i++) { + for ($i = 0, $l = is_countable($value) ? count($value) : 0; $i < $l && $html_len < 30; $i++) { $html .= (!empty($html)) ? '; ' : ''; $html .= join(', ', (array)$value[$i]); diff --git a/lib/DisplayType/tableStatic.class.inc b/lib/DisplayType/tableStatic.class.inc index 50cfd1b6..0d381020 100644 --- a/lib/DisplayType/tableStatic.class.inc +++ b/lib/DisplayType/tableStatic.class.inc @@ -20,7 +20,7 @@ class PXDisplayTypeTableStatic extends PXDisplayTypeStatic foreach ($row as $cell) { is_array($cell) && $cell = print_r($cell, true); - $safe && $cell = htmlspecialchars($cell, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); + $safe && $cell = htmlspecialchars((string) $cell, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); $_html .= ''; $_html .= $cell; $_html .= ''; @@ -34,4 +34,3 @@ class PXDisplayTypeTableStatic extends PXDisplayTypeStatic return $_html; } } -?> diff --git a/lib/DisplayType/text.class.inc b/lib/DisplayType/text.class.inc index 9af17198..57614974 100644 --- a/lib/DisplayType/text.class.inc +++ b/lib/DisplayType/text.class.inc @@ -14,10 +14,10 @@ class PXDisplayTypeText extends PXDisplayType { public function buildInput($field, $object, $param = NULL) { - $value = isset($object[$field->name]) ? $object[$field->name] : NULL; + $value = $object[$field->name] ?? NULL; $height = isset($field->displayTypeArgs[1]) ? (int)$field->displayTypeArgs[1] : 0; $maxlength = !empty($field->displayTypeArgs[2]) ? (int)$field->displayTypeArgs[2] : NULL; - $wrap = !empty($field->displayTypeArgs[3]) ? trim($field->displayTypeArgs[3]) : "soft"; + $wrap = !empty($field->displayTypeArgs[3]) ? trim((string) $field->displayTypeArgs[3]) : "soft"; return NLAbstractHTMLForm::BuildText($field->name, $value, $height, $maxlength, $wrap); } @@ -25,13 +25,13 @@ class PXDisplayTypeText extends PXDisplayType public function buildCell($field, $object, $param = NULL) { $trunc = isset($field->displayTypeArgs[2]) && (int)$field->displayTypeArgs[2] > 0 ? (int)$field->displayTypeArgs[2] : 50; - $value = isset($object[$field->name]) ? $object[$field->name] : ''; + $value = $object[$field->name] ?? ''; $value = $this->getSubDirectoryIfExists($field, $object, $value); - if (mb_strlen($value) > $trunc) { - $value = htmlspecialchars(mb_substr($value, 0, $trunc), ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) . '…'; + if (mb_strlen((string) $value) > $trunc) { + $value = htmlspecialchars(mb_substr((string) $value, 0, $trunc), ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) . '…'; } else { - $value = htmlspecialchars($value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); + $value = htmlspecialchars((string) $value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); } return $value; diff --git a/lib/Engine/json.class.inc b/lib/Engine/json.class.inc index cdac888e..a9c2d4f3 100644 --- a/lib/Engine/json.class.inc +++ b/lib/Engine/json.class.inc @@ -36,7 +36,7 @@ class PXEngineJSON extends AbstractEngine } } - public function sendJson() + public function sendJson(): void { $response = Response::getInstance(); $response->sendJson($this->result); diff --git a/lib/Engine/preview.class.inc b/lib/Engine/preview.class.inc index 2141f055..45404a50 100644 --- a/lib/Engine/preview.class.inc +++ b/lib/Engine/preview.class.inc @@ -5,10 +5,7 @@ class PXPreviewEngine extends PXEngineIndex { // @overload protected $db = ['factory' => 'PXPreviewDB', 'helper' => true]; - private $path; - - public function __construct($path) { - $this->path = $path; + public function __construct(private $path) { parent::__construct(); } diff --git a/lib/Engine/sbin.class.inc b/lib/Engine/sbin.class.inc index cb649917..d00c1d71 100644 --- a/lib/Engine/sbin.class.inc +++ b/lib/Engine/sbin.class.inc @@ -15,7 +15,7 @@ class PXEngineSbin extends AbstractEngine /** * @var Symfony\Component\HttpFoundation\Session\Session */ - protected $session = ['factory' => 'Symfony\Component\HttpFoundation\Session\Session', 'helper' => true]; + protected $session = ['factory' => \Symfony\Component\HttpFoundation\Session\Session::class, 'helper' => true]; protected $initOrder = ['container', 'app', 'db', 'request', 'session', 'user', 'layout']; diff --git a/lib/Filesys/dir.class.inc b/lib/Filesys/dir.class.inc index 6ebea87d..229cd62b 100644 --- a/lib/Filesys/dir.class.inc +++ b/lib/Filesys/dir.class.inc @@ -50,10 +50,10 @@ class NLDir $catalog = $this->_prepare($catalog); if (DIRECTORY_SEPARATOR === '\\') { - if ($catalog{1} == ':' && $catalog{2} == '\\') { + if ($catalog[1] == ':' && $catalog[2] == '\\') { $this->name = $catalog; - } elseif ($catalog{0} . $catalog{1} === '\\\\') { + } elseif ($catalog[0] . $catalog[1] === '\\\\') { $this->name = $catalog; } else { @@ -61,7 +61,7 @@ class NLDir } } else { - if ($catalog{0} !== '/') { + if ($catalog[0] !== '/') { $this->name = getcwd() . '/' . $catalog . '/'; } else { $this->name = $catalog . '/'; @@ -79,7 +79,7 @@ class NLDir { $ds = DIRECTORY_SEPARATOR; - $catalog = preg_replace('|^\\\\|' . REGEX_MOD, '||', $catalog); + $catalog = preg_replace('|^\\\\|' . REGEX_MOD, '||', (string) $catalog); $catalog = str_replace('\\', '/', $catalog); $catalog = preg_replace('|\/{2,}|' . REGEX_MOD, '/', $catalog); @@ -149,7 +149,7 @@ class NLDir while (true) { $entry = $this->_dirObject->read(); - if ($entry{0} != '.') { + if ($entry[0] != '.') { break; } } @@ -237,7 +237,8 @@ class NLDir public function unlink($entry) { - ini_set('track_errors', 1); + $php_errormsg = null; + ini_set('track_errors', 1); $oldError = error_reporting(0); $res = @unlink($entry); diff --git a/lib/Filesys/listing.class.inc b/lib/Filesys/listing.class.inc index a52d699b..c9d6b5b0 100644 --- a/lib/Filesys/listing.class.inc +++ b/lib/Filesys/listing.class.inc @@ -69,7 +69,7 @@ class PXFileListing public function html() { - $up = preg_replace('|[^/]+/$|' . REGEX_MOD, '', $this->_catalog); + $up = preg_replace('|[^/]+/$|' . REGEX_MOD, '', (string) $this->_catalog); $canBrowse = $this->_isValid($this->_catalog); $canUp = $this->_isValid($up); @@ -160,7 +160,7 @@ class PXFileListing } foreach ($this->_roots as $root) { - if (mb_strpos($this->_getRealPath($catalog), $this->_getRealPath($root)) === 0 && mb_strlen($root) <= mb_strlen($catalog)) { + if (mb_strpos((string) $this->_getRealPath($catalog), (string) $this->_getRealPath($root)) === 0 && mb_strlen((string) $root) <= mb_strlen((string) $catalog)) { return true; } } @@ -170,11 +170,11 @@ class PXFileListing public function _prepare($tmp) { - $tmp = str_replace('\\\\', '||', $tmp); + $tmp = str_replace('\\\\', '||', (string) $tmp); $tmp = str_replace('//', '||', $tmp); $tmp = str_replace('\\', '/', $tmp); $tmp .= '/'; - $tmp = preg_replace('|/{2,}|' . REGEX_MOD, '/', $tmp); + $tmp = preg_replace('|/{2,}|' . REGEX_MOD, '/', (string) $tmp); $tmp = str_replace('||', '\\\\', $tmp); return $tmp; @@ -182,13 +182,13 @@ class PXFileListing public function _isSmbShare($path) { - return mb_strpos($path, '\\\\') === 0; + return mb_strpos((string) $path, '\\\\') === 0; } public function _fullPath($catalog) { if ($this->_isSmbShare($catalog)) { - $catalog = str_replace('/', '\\', $catalog); + $catalog = str_replace('/', '\\', (string) $catalog); return $catalog; } @@ -258,7 +258,7 @@ class PXFileListingItem public function _Type() { - preg_match('|[^/.\\\\]+$|' . REGEX_MOD, $this->name, $tmp); + preg_match('|[^/.\\\\]+$|' . REGEX_MOD, (string) $this->name, $tmp); $this->type = isset($tmp[0]) ? mb_strtolower($tmp[0]) : ''; } @@ -292,7 +292,7 @@ class PXFileListingItem $htdocsPrefix = null; foreach (['site', 'local', 'libpp'] as $prefix) { - if (mb_strpos($this->catalog, $prefix . '/htdocs') === 0) { + if (mb_strpos((string) $this->catalog, $prefix . '/htdocs') === 0) { $htdocsPrefix = $prefix . '/htdocs'; break; } @@ -302,7 +302,7 @@ class PXFileListingItem return; } - $this->alias = str_replace($htdocsPrefix, '', $this->catalog . rawurlencode($this->name)); + $this->alias = str_replace($htdocsPrefix, '', $this->catalog . rawurlencode((string) $this->name)); } public function isRoot() diff --git a/lib/HTML/Abstract/htmlform.class.inc b/lib/HTML/Abstract/htmlform.class.inc index d5fba3bc..39e92bfe 100644 --- a/lib/HTML/Abstract/htmlform.class.inc +++ b/lib/HTML/Abstract/htmlform.class.inc @@ -169,6 +169,7 @@ class NLAbstractHTMLForm public static function BuildMultimedia($name, $value, $type = 'image', $entry = NULL) { + $input = null; switch ($type) { case 'image': $input = new PXInputImage($name, $value, $entry); diff --git a/lib/HTML/Admin/Decorators/widgetscollection.class.inc b/lib/HTML/Admin/Decorators/widgetscollection.class.inc index ef595bf4..5b70a29f 100644 --- a/lib/HTML/Admin/Decorators/widgetscollection.class.inc +++ b/lib/HTML/Admin/Decorators/widgetscollection.class.inc @@ -3,9 +3,9 @@ final class PXDecorativeWidgetsCollection { - private static $namedData = []; - private static $objectsNameHash = []; - private static $objectsTypeHash = []; + private static array $namedData = []; + private static array $objectsNameHash = []; + private static array $objectsTypeHash = []; private function __construct() { @@ -13,7 +13,7 @@ final class PXDecorativeWidgetsCollection private static function hash_key($object) { - return strtolower(is_object($object) ? get_class($object) : $object); + return strtolower(is_object($object) ? $object::class : $object); } private static function removeFromCollection($hash, $key) @@ -72,7 +72,8 @@ final class PXDecorativeWidgetsCollection "wname" => self::hash_key($widget), "key" => self::hash_key($classToDecorate), "subkey" => self::hash_key($decorableAreal), - "alias" => self::hash_key($alias)]; + "alias" => self::hash_key($alias) + ]; } private static function putInLists(&$widget, $key, $wname, $subkey, $alias) @@ -84,7 +85,8 @@ final class PXDecorativeWidgetsCollection public static function addToCollection($widget, $classToDecorate, $decorableAreal, $decorablePart, $alias = 'default') { $key = $subkey = $wname = null; - extract(self::initVars($widget, $classToDecorate, $decorableAreal, $decorablePart, $alias)); + $initVars = self::initVars($widget, $classToDecorate, $decorableAreal, $decorablePart, $alias); + extract($initVars); self::$namedData[$key][$subkey][$decorablePart][] = &$widget; self::putInLists($widget, $key, $wname, $subkey, $alias); @@ -93,7 +95,8 @@ final class PXDecorativeWidgetsCollection public static function addToCollectionBefore($beforeAreal, $widget, $classToDecorate, $decorableAreal, $decorablePart, $alias = 'default') { $key = $subkey = $wname = null; - extract(self::initVars($widget, $classToDecorate, $decorableAreal, $decorablePart, $alias)); + $initVars = self::initVars($widget, $classToDecorate, $decorableAreal, $decorablePart, $alias); + extract($initVars); self::addToPosition(0, $beforeAreal, $widget, $classToDecorate, $decorableAreal, $decorablePart, $alias); self::putInLists($widget, $key, $wname, $subkey, $alias); @@ -102,7 +105,8 @@ final class PXDecorativeWidgetsCollection public static function addToCollectionAfter($afterAreal, $widget, $classToDecorate, $decorableAreal, $decorablePart, $alias = 'default') { $key = $subkey = $wname = null; - extract(self::initVars($widget, $classToDecorate, $decorableAreal, $decorablePart, $alias)); + $initVars = self::initVars($widget, $classToDecorate, $decorableAreal, $decorablePart, $alias); + extract($initVars); self::addToPosition(1, $afterAreal, $widget, $classToDecorate, $decorableAreal, $decorablePart, $alias); self::putInLists($widget, $key, $wname, $subkey, $alias); @@ -111,9 +115,10 @@ final class PXDecorativeWidgetsCollection private static function addToPosition($shift, $desiredAreal, &$widget, $classToDecorate, $decorableAreal, $decorablePart, $alias = 'default') { $key = $subkey = $wname = null; - extract(self::initVars($widget, $classToDecorate, $decorableAreal, $decorablePart, $alias)); + $initVars = self::initVars($widget, $classToDecorate, $decorableAreal, $decorablePart, $alias); + extract($initVars); - $desiredAreal = strtolower($desiredAreal); + $desiredAreal = strtolower((string) $desiredAreal); $merged = [$subkey => [$decorablePart => [&$widget]]]; if (!isset(self::$namedData[$key])) { @@ -148,5 +153,3 @@ final class PXDecorativeWidgetsCollection return self::removeFromCollection('objectsTypeHash', self::hash_key($decorableAreal) . self::hash_key($alias)); } } - -?> diff --git a/lib/HTML/Admin/Widgets/Control/button.class.inc b/lib/HTML/Admin/Widgets/Control/button.class.inc index c8973e8f..b57ed021 100644 --- a/lib/HTML/Admin/Widgets/Control/button.class.inc +++ b/lib/HTML/Admin/Widgets/Control/button.class.inc @@ -3,14 +3,12 @@ class PXControlButton extends PXAdminWidget { - public $value; public $onClickCode = ''; public $cssClass = ''; - public function __construct($value) - { - $this->value = $value; - } + public function __construct(public $value) + { + } /** * @param string $onClickCode @@ -39,8 +37,8 @@ class PXControlButton extends PXAdminWidget */ public function html() { - $cssClass = mb_strlen($this->cssClass) ? ' class="' . $this->cssClass . '"' : ''; - $onClick = mb_strlen($this->onClickCode) ? ' onclick="' . $this->onClickCode . '"' : ''; + $cssClass = mb_strlen((string) $this->cssClass) ? ' class="' . $this->cssClass . '"' : ''; + $onClick = mb_strlen((string) $this->onClickCode) ? ' onclick="' . $this->onClickCode . '"' : ''; return 'disable . '>' . $this->value . ''; } diff --git a/lib/HTML/Admin/Widgets/Control/close.class.inc b/lib/HTML/Admin/Widgets/Control/close.class.inc index 053a093e..e5e3f40b 100644 --- a/lib/HTML/Admin/Widgets/Control/close.class.inc +++ b/lib/HTML/Admin/Widgets/Control/close.class.inc @@ -2,11 +2,8 @@ class PXControlClose extends PXAdminWidget { - public $value; - - public function __construct($value) + public function __construct(public $value) { - $this->value = $value; } public function html() diff --git a/lib/HTML/Admin/Widgets/Control/reset.class.inc b/lib/HTML/Admin/Widgets/Control/reset.class.inc index 490d84c9..2fd2c4d2 100644 --- a/lib/HTML/Admin/Widgets/Control/reset.class.inc +++ b/lib/HTML/Admin/Widgets/Control/reset.class.inc @@ -2,11 +2,8 @@ class PXControlReset extends PXAdminWidget { - public $value; - - public function __construct($value) + public function __construct(public $value) { - $this->value = $value; } public function html() diff --git a/lib/HTML/Admin/Widgets/Control/submit.class.inc b/lib/HTML/Admin/Widgets/Control/submit.class.inc index 0b4cd87b..7083bfd9 100644 --- a/lib/HTML/Admin/Widgets/Control/submit.class.inc +++ b/lib/HTML/Admin/Widgets/Control/submit.class.inc @@ -2,15 +2,8 @@ class PXControlSubmit extends PXAdminWidget { - public $value; - public $name = null; - public $accessKey = null; - - public function __construct($value, $name = null, $accessKey = null) + public function __construct(public $value, public $name = null, public $accessKey = null) { - $this->value = $value; - $this->name = $name; - $this->accessKey = $accessKey; } public function html() diff --git a/lib/HTML/Admin/Widgets/FieldGroups/table.class.inc b/lib/HTML/Admin/Widgets/FieldGroups/table.class.inc index 27ccaf7f..139102b6 100644 --- a/lib/HTML/Admin/Widgets/FieldGroups/table.class.inc +++ b/lib/HTML/Admin/Widgets/FieldGroups/table.class.inc @@ -10,7 +10,7 @@ class PXWidgetFieldGroupsTableView extends PXWidgetFieldGroups public function html() { $html = ''; - if (mb_strlen($this->groupName)) { + if (mb_strlen((string) $this->groupName)) { $html .= ''; } foreach ($this->group as $field) { diff --git a/lib/HTML/Admin/Widgets/Input/Datetime/abstract.class.inc b/lib/HTML/Admin/Widgets/Input/Datetime/abstract.class.inc index bd851dc8..7f2d3e52 100644 --- a/lib/HTML/Admin/Widgets/Input/Datetime/abstract.class.inc +++ b/lib/HTML/Admin/Widgets/Input/Datetime/abstract.class.inc @@ -2,12 +2,10 @@ // Abstract class PXInputDateTime extends PXAdminWidget { - public $name; public $date; // array(year, month, day, hour, minute, second); - public function __construct($name, $value, $notNull = true) + public function __construct(public $name, $value, $notNull = true) { - $this->name = $name; $this->notNull = $notNull; $this->date = (is_array($value)) ? $value : $this->_parseDate($value, $notNull); @@ -28,7 +26,7 @@ class PXInputDateTime extends PXAdminWidget if (!is_null($value)) { $matches = []; - $value = trim($value); + $value = trim((string) $value); $value = mb_substr($value, 0, 19); preg_match("/^((\d{1,2}).(\d{1,2}).(\d{4}))?\s?((\d{1,2})(:(\d{1,2})(:(\d{1,2}))?)?)?/" . REGEX_MOD, trim($value), $matches); diff --git a/lib/HTML/Admin/Widgets/Input/Datetime/datecalendar.class.inc b/lib/HTML/Admin/Widgets/Input/Datetime/datecalendar.class.inc index 6a2ac6e9..d29d8e43 100644 --- a/lib/HTML/Admin/Widgets/Input/Datetime/datecalendar.class.inc +++ b/lib/HTML/Admin/Widgets/Input/Datetime/datecalendar.class.inc @@ -7,7 +7,7 @@ class PXInputDateCalendar extends PXInputDateTime public function html() { $name = $this->name; - $saneName = str_replace(['[', ']'], '', $name); + $saneName = str_replace(['[', ']'], '', (string) $name); $day = is_null($this->date['day']) ? null : sprintf('%02d', $this->date['day']); $month = is_null($this->date['day']) ? null : sprintf('%02d', $this->date['month']); diff --git a/lib/HTML/Admin/Widgets/Input/Datetime/time.class.inc b/lib/HTML/Admin/Widgets/Input/Datetime/time.class.inc index 121055f8..2f26214d 100644 --- a/lib/HTML/Admin/Widgets/Input/Datetime/time.class.inc +++ b/lib/HTML/Admin/Widgets/Input/Datetime/time.class.inc @@ -8,10 +8,10 @@ class PXInputTime extends PXInputDateTime { $null = [null => '--']; - $hours = array_map([$this, '__leadingZero'], range(0, 23)); + $hours = array_map($this->__leadingZero(...), range(0, 23)); $hours = $null + array_combine($hours, $hours); - $sixty = array_map([$this, '__leadingZero'], range(0, 59)); + $sixty = array_map($this->__leadingZero(...), range(0, 59)); $seconds = $minutes = $null + array_combine($sixty, $sixty); $hour = new PXInputDropDown($this->name . '[hour]', $this->__leadingZero($this->date['hour']), $hours); diff --git a/lib/HTML/Admin/Widgets/Input/checkbox.class.inc b/lib/HTML/Admin/Widgets/Input/checkbox.class.inc index 216327c0..0efd4d8b 100644 --- a/lib/HTML/Admin/Widgets/Input/checkbox.class.inc +++ b/lib/HTML/Admin/Widgets/Input/checkbox.class.inc @@ -2,14 +2,12 @@ class PXInputCheckbox extends PXAdminWidget { - public $name; public $on; public $value = NULL; public $id = NULL; - public function __construct($name, $on = false, $value = NULL) + public function __construct(public $name, $on = false, $value = NULL) { - $this->name = $name; $this->setOn($on); $this->setValue($value); } diff --git a/lib/HTML/Admin/Widgets/Input/color.class.inc b/lib/HTML/Admin/Widgets/Input/color.class.inc index 03379f7e..6adedfb7 100644 --- a/lib/HTML/Admin/Widgets/Input/color.class.inc +++ b/lib/HTML/Admin/Widgets/Input/color.class.inc @@ -2,23 +2,18 @@ class PXInputColor extends PXAdminWidget { - public $name; - public $value; - - public function __construct($name, $value) + public function __construct(public $name, public $value) { - $this->name = $name; - $this->value = $value; } public function html() { $name = $this->name; - $value = htmlspecialchars($this->value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); + $value = htmlspecialchars((string) $this->value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); - $red = hexdec(mb_substr($this->value, 0, 2)); - $green = hexdec(mb_substr($this->value, 2, 2)); - $blue = hexdec(mb_substr($this->value, 4, 2)); + $red = hexdec(mb_substr((string) $this->value, 0, 2)); + $green = hexdec(mb_substr((string) $this->value, 2, 2)); + $blue = hexdec(mb_substr((string) $this->value, 4, 2)); return << diff --git a/lib/HTML/Admin/Widgets/Input/dropdown.class.inc b/lib/HTML/Admin/Widgets/Input/dropdown.class.inc index 277c297c..ca27ac4a 100644 --- a/lib/HTML/Admin/Widgets/Input/dropdown.class.inc +++ b/lib/HTML/Admin/Widgets/Input/dropdown.class.inc @@ -2,20 +2,13 @@ class PXInputDropDown extends PXAdminWidget { - public $name; - public $value; - public $values = []; - public $multiple = ''; public $size = ''; protected $empty = [null => 0]; - public function __construct($name, $value, $values) + public function __construct(public $name, public $value, public $values) { - $this->name = $name; - $this->value = $value; - $this->values = $values; } public function setMultiple($multiple) @@ -77,7 +70,7 @@ class PXInputDropDown extends PXAdminWidget } return sprintf('', quot($value), $isSelected ? ' selected' : '', - htmlspecialchars($title, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) + htmlspecialchars((string) $title, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) ); } } diff --git a/lib/HTML/Admin/Widgets/Input/file.class.inc b/lib/HTML/Admin/Widgets/Input/file.class.inc index 80f5a5e8..31428fa8 100644 --- a/lib/HTML/Admin/Widgets/Input/file.class.inc +++ b/lib/HTML/Admin/Widgets/Input/file.class.inc @@ -4,17 +4,12 @@ require_once PPLIBPATH . 'Common/functions.html.inc'; class PXInputFile extends PXAdminWidget { - public $value = []; - public $name; - public $entry; - public $distension; + public string $distension = ''; - public function __construct($name, $value, $entry = NULL) + public string $type = 'file'; + + public function __construct(public $name, public $value, public $entry = NULL) { - $this->value = $value; - $this->name = $name; - $this->entry = $entry; - $this->type = 'file'; } public function setparam() @@ -52,7 +47,7 @@ HTML; $html .= '
'; - $picName = mb_substr($this->value['path'], mb_strrpos($this->value['path'], '/') + 1); + $picName = mb_substr((string) $this->value['path'], mb_strrpos((string) $this->value['path'], '/') + 1); $html .= 'disable . '>'; $html .= ''; } else { @@ -65,7 +60,7 @@ HTML; { $this->setparam(); - $name = $this->name . (!is_null($this->entry) && mb_strlen($this->entry) ? '_edit' : '') . $this->distension; + $name = $this->name . (!is_null($this->entry) && mb_strlen((string) $this->entry) ? '_edit' : '') . $this->distension; return html_block('imagepreview', [], [ $this->preview(), diff --git a/lib/HTML/Admin/Widgets/Input/hidden.class.inc b/lib/HTML/Admin/Widgets/Input/hidden.class.inc index 22c2472e..f6281379 100644 --- a/lib/HTML/Admin/Widgets/Input/hidden.class.inc +++ b/lib/HTML/Admin/Widgets/Input/hidden.class.inc @@ -2,19 +2,14 @@ class PXInputHidden extends PXAdminWidget { - public $value; - public $name; - - public function __construct($name, $value) + public function __construct(public $name, public $value) { - $this->value = $value; - $this->name = $name; } public function html() { - $name = htmlspecialchars($this->name, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); - $value = htmlspecialchars($this->value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); + $name = htmlspecialchars((string) $this->name, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); + $value = htmlspecialchars((string) $this->value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); return 'disable . '>'; } diff --git a/lib/HTML/Admin/Widgets/Input/image.class.inc b/lib/HTML/Admin/Widgets/Input/image.class.inc index 404f4e26..327d8412 100644 --- a/lib/HTML/Admin/Widgets/Input/image.class.inc +++ b/lib/HTML/Admin/Widgets/Input/image.class.inc @@ -20,9 +20,9 @@ class PXInputImage extends PXInputFile { $this->distension = is_null($this->entry) ? '' : '[' . $this->entry . ']'; - $w = isset($this->value['width']) ? $this->value['width'] : '100'; - $h = isset($this->value['height']) ? $this->value['height'] : '100'; - $this->p = isset($this->value['path']) ? $this->value['path'] : $this->_default; + $w = $this->value['width'] ?? '100'; + $h = $this->value['height'] ?? '100'; + $this->p = $this->value['path'] ?? $this->_default; if ($w > 100 || $h > 100) { $this->width = ($w - $h) > 0 ? 100 : floor(100 * $w / $h); @@ -58,4 +58,3 @@ HTML; } } -?> diff --git a/lib/HTML/Admin/Widgets/Input/input.class.inc b/lib/HTML/Admin/Widgets/Input/input.class.inc index 257531a0..221911fb 100644 --- a/lib/HTML/Admin/Widgets/Input/input.class.inc +++ b/lib/HTML/Admin/Widgets/Input/input.class.inc @@ -5,21 +5,6 @@ */ class PXInputInput extends PXAdminWidget { - /** - * @var string - */ - public $value; - - /** - * @var string - */ - public $name; - - /** - * @var int|null - */ - public $maxlength = null; - /** * @var array */ @@ -37,11 +22,9 @@ class PXInputInput extends PXAdminWidget { * @param string $value * @param int|null $maxlength */ - public function __construct($name, $value, $maxlength = null) { - $this->value = $value; - $this->name = $name; - $this->maxlength = $maxlength; - } + public function __construct(public $name, public $value, public ?int $maxlength = null) + { + } /** * @return string @@ -67,10 +50,9 @@ class PXInputInput extends PXAdminWidget { } /** - * @param array $classes - * @return PXInputInput - */ - public function setClasses(array $classes) { + * @return PXInputInput + */ + public function setClasses(array $classes) { $this->classes = $classes; return $this; } diff --git a/lib/HTML/Admin/Widgets/Input/ip.class.inc b/lib/HTML/Admin/Widgets/Input/ip.class.inc index c95a07ec..9438c210 100644 --- a/lib/HTML/Admin/Widgets/Input/ip.class.inc +++ b/lib/HTML/Admin/Widgets/Input/ip.class.inc @@ -2,13 +2,8 @@ class PXInputIPAddr extends PXAdminWidget { - public $value; - public $name; - - public function __construct($name, $value) + public function __construct(public $name, public $value) { - $this->value = $value; - $this->name = $name; } public function html() diff --git a/lib/HTML/Admin/Widgets/Input/link.class.inc b/lib/HTML/Admin/Widgets/Input/link.class.inc index 9fd3a4b5..3f9eb81e 100644 --- a/lib/HTML/Admin/Widgets/Input/link.class.inc +++ b/lib/HTML/Admin/Widgets/Input/link.class.inc @@ -2,19 +2,14 @@ class PXInputLinkToFile extends PXAdminWidget { - public $name; - public $value; - - public function __construct($name, $value) + public function __construct(public $name, public $value) { - $this->name = $name; - $this->value = $value; } public function html() { - $lastDir = isset($_COOKIE['fileManagerDir']) && mb_strlen(trim($_COOKIE['fileManagerDir'])) ? 'htdocs' . trim( - $_COOKIE['fileManagerDir'] + $lastDir = isset($_COOKIE['fileManagerDir']) && mb_strlen(trim((string) $_COOKIE['fileManagerDir'])) ? 'htdocs' . trim( + (string) $_COOKIE['fileManagerDir'] ) : ''; $html = '
'; diff --git a/lib/HTML/Admin/Widgets/Input/parentdropdown.class.inc b/lib/HTML/Admin/Widgets/Input/parentdropdown.class.inc index 1b89beb1..72269d27 100644 --- a/lib/HTML/Admin/Widgets/Input/parentdropdown.class.inc +++ b/lib/HTML/Admin/Widgets/Input/parentdropdown.class.inc @@ -2,16 +2,8 @@ class PXInputParentDropDown extends PXAdminWidget { - public $name; - public $value; - public $values = []; - - public function __construct($name, $value, $values) + public function __construct(public $name, public $value, public $values) { - $this->name = $name; - $this->value = $value; - $this->values = $values; - } public function html() @@ -27,7 +19,7 @@ class PXInputParentDropDown extends PXAdminWidget $html .= ''; @@ -38,7 +30,7 @@ class PXInputParentDropDown extends PXAdminWidget $html .= sprintf( '', quot($this->value), - htmlspecialchars($this->value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) + htmlspecialchars((string) $this->value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) ); } diff --git a/lib/HTML/Admin/Widgets/Input/password.class.inc b/lib/HTML/Admin/Widgets/Input/password.class.inc index b3b1614e..95fc2093 100644 --- a/lib/HTML/Admin/Widgets/Input/password.class.inc +++ b/lib/HTML/Admin/Widgets/Input/password.class.inc @@ -2,14 +2,10 @@ class PXInputPassword extends PXAdminWidget { - public $value; - public $name; public $canDelete = true; - public function __construct($name, $value) + public function __construct(public $name, public $value) { - $this->value = $value; - $this->name = $name; } public function canDelete($canDelete) @@ -21,7 +17,7 @@ class PXInputPassword extends PXAdminWidget { $name = $this->name; - if (mb_strlen($this->value) && $this->canDelete) { + if (mb_strlen((string) $this->value) && $this->canDelete) { $deleteHTML = <<disable}> diff --git a/lib/HTML/Admin/Widgets/Input/radiobutton.class.inc b/lib/HTML/Admin/Widgets/Input/radiobutton.class.inc index 7f9ab593..ff9c0899 100644 --- a/lib/HTML/Admin/Widgets/Input/radiobutton.class.inc +++ b/lib/HTML/Admin/Widgets/Input/radiobutton.class.inc @@ -2,7 +2,7 @@ class PXInputRadiobutton extends PXInputCheckbox { - private $pattern = ''; + private string $pattern = ''; public function html() { $atts = $this->on; diff --git a/lib/HTML/Admin/Widgets/Input/radiolist.class.inc b/lib/HTML/Admin/Widgets/Input/radiolist.class.inc index 16697d41..1139ddc9 100644 --- a/lib/HTML/Admin/Widgets/Input/radiolist.class.inc +++ b/lib/HTML/Admin/Widgets/Input/radiolist.class.inc @@ -2,16 +2,12 @@ class PXInputRadiolist extends PXAdminWidget { - private $name = null; - private $selected = null; - private $list = null; + private ?string $selected = null; - private $pattern = ''; + private string $pattern = ''; - public function __construct($name, $selected, $list) { - $this->name = $name; + public function __construct(private $name, $selected, private $list) { $this->selected = (string)$selected; - $this->list = $list; } public function html() { @@ -22,10 +18,10 @@ class PXInputRadiolist extends PXAdminWidget { $out = ''; foreach ($this->list as $value => $title) { // hm... I don't like it - $id = str_replace(['[', ']'], ['_', ''], $this->name) . '_' . preg_replace( + $id = str_replace(['[', ']'], ['_', ''], (string) $this->name) . '_' . preg_replace( '/[^a-z0-9\-]/i' . REGEX_MOD, '', - $value + (string) $value ); $input = new PXInputRadioButton($this->name, $this->selected === (string)$value, $value); $input->setId($id); diff --git a/lib/HTML/Admin/Widgets/Input/richedit.class.inc b/lib/HTML/Admin/Widgets/Input/richedit.class.inc index 71690a2b..682a59f5 100644 --- a/lib/HTML/Admin/Widgets/Input/richedit.class.inc +++ b/lib/HTML/Admin/Widgets/Input/richedit.class.inc @@ -2,24 +2,19 @@ class PXInputRichEdit extends PXAdminComplexWidget { - public $value; - public $name; - public $height; public $container; public $toLayout; protected $editors = []; + protected $favoriteEditor = null; + public const AFTER_TEXTAREA = 0; - public function __construct($name, $value, $height) + public function __construct(public $name, public $value, public $height) { - $this->value = $value; - $this->name = $name; - $this->height = $height; $this->container = $name . '_id'; $this->toLayout = PXRegistry::getLayout(); - $this->favoriteEditor = null; //default value $this->blocks = [self::AFTER_TEXTAREA => []]; } @@ -58,7 +53,7 @@ class PXInputRichEdit extends PXAdminComplexWidget { $this->decorate('RICHEDITORS'); - array_walk($this->blocks[self::AFTER_TEXTAREA], [$this, 'initiate']); + array_walk($this->blocks[self::AFTER_TEXTAREA], $this->initiate(...)); $this->addEditor('Без редактора'); $embedding = $this->getTypesDict(); @@ -67,7 +62,7 @@ class PXInputRichEdit extends PXAdminComplexWidget $richEditor = isset($this->blocks[self::AFTER_TEXTAREA][$embedding['current']]) ? $this->blocks[self::AFTER_TEXTAREA][$embedding['current']]->html() : ''; - $escapedValue = "\n" . htmlspecialchars($this->value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); + $escapedValue = "\n" . htmlspecialchars((string) $this->value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET); return << diff --git a/lib/HTML/Admin/Widgets/Input/table.class.inc b/lib/HTML/Admin/Widgets/Input/table.class.inc index ad6204d6..ea9ecaa7 100644 --- a/lib/HTML/Admin/Widgets/Input/table.class.inc +++ b/lib/HTML/Admin/Widgets/Input/table.class.inc @@ -2,19 +2,11 @@ class PXInputTable extends PXAdminWidget { - public $name; - public $value; public $colsWidth; - public $height; - public $header; - public function __construct($name, $value, $cols, $height, $header = []) + public function __construct(public $name, public $value, $cols, public $height, public $header = []) { - $this->name = $name; - $this->value = $value; $this->value[] = null; - $this->height = $height; - $this->header = $header; if (is_array($cols)) { $this->colsWidth = $cols; @@ -37,7 +29,7 @@ class PXInputTable extends PXAdminWidget public function colgroup() { $html = '
'; - for ($i = 0; $i < count($this->colsWidth); $i++) { + for ($i = 0; $i < (is_countable($this->colsWidth) ? count($this->colsWidth) : 0); $i++) { $width = ceil(100 * $this->colsWidth[$i] / array_sum($this->colsWidth)); $html .= ''; } @@ -63,10 +55,10 @@ class PXInputTable extends PXAdminWidget $row = (array)$row; - for ($i = 0, $cols = count($this->colsWidth); $i < $cols; $i++) { + for ($i = 0, $cols = is_countable($this->colsWidth) ? count($this->colsWidth) : 0; $i < $cols; $i++) { $html .= ' @@ -205,324 +204,281 @@ public function _setInnerLayout()
' . $this->groupName . '
'; - $currentValue = (isset($row[$i])) ? $row[$i] : null; + $currentValue = $row[$i] ?? null; if ($this->height == 0) { $text = new PXInputInput($this->name . '[' . $idx . '][]', $currentValue); diff --git a/lib/HTML/Admin/Widgets/Input/textarea.class.inc b/lib/HTML/Admin/Widgets/Input/textarea.class.inc index 2644c5f7..8ba79ab7 100644 --- a/lib/HTML/Admin/Widgets/Input/textarea.class.inc +++ b/lib/HTML/Admin/Widgets/Input/textarea.class.inc @@ -2,24 +2,13 @@ class PXInputTextarea extends PXAdminWidget { - public $value; - public $name; - public $height; - public $maxlength; - public $wrap; - - public function __construct($name, $value, $height, $maxlength = null, $wrap = "soft") + public function __construct(public $name, public $value, public $height, public $maxlength = null, public $wrap = "soft") { - $this->value = $value; - $this->name = $name; - $this->height = $height; - $this->maxlength = $maxlength; //Only Chrome and FF support this attribute - $this->wrap = $wrap; } public function html() { - $height = strpos($this->height, '%') ? $this->height : $this->height . 'px'; + $height = strpos((string) $this->height, '%') ? $this->height : $this->height . 'px'; $max = isset($this->maxlength) ? 'maxlength="' . (int)$this->maxlength . '"' : ''; return sprintf( @@ -29,7 +18,7 @@ class PXInputTextarea extends PXAdminWidget $height, $this->disable, $max, - "\n" . htmlspecialchars($this->value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) + "\n" . htmlspecialchars((string) $this->value, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) ); } } diff --git a/lib/HTML/Admin/Widgets/Objects/abstract.class.inc b/lib/HTML/Admin/Widgets/Objects/abstract.class.inc index bc2175ca..c8af5287 100644 --- a/lib/HTML/Admin/Widgets/Objects/abstract.class.inc +++ b/lib/HTML/Admin/Widgets/Objects/abstract.class.inc @@ -77,7 +77,7 @@ abstract class PXAdminObjects extends PXAdminWidget implements IPXAdminObjects $this->layoutCell = $cellName; - $cell = explode('.', $cellName); + $cell = explode('.', (string) $cellName); $cell[2]++; $this->layoutButtonCell = implode('.', $cell); diff --git a/lib/HTML/Admin/Widgets/Objects/ajaxtree.class.inc b/lib/HTML/Admin/Widgets/Objects/ajaxtree.class.inc index 46aed423..b33edbbe 100644 --- a/lib/HTML/Admin/Widgets/Objects/ajaxtree.class.inc +++ b/lib/HTML/Admin/Widgets/Objects/ajaxtree.class.inc @@ -1,10 +1,7 @@ currentBranch = $currentBranch; + public function __construct($format, $where=NULL, $loadMethod='', $withoutExternals = false, protected $currentBranch = null) { parent::__construct($format, $where, $loadMethod, $withoutExternals); } @@ -15,7 +12,7 @@ class PXAdminAjaxTreeObjects extends PXAdminTreeObjects { $this->widget = new PXAdminAjaxTree($this->format, $this->tree, $this->currentBranch); $this->widget->setOpenedLeafs($this->request->getLeafStatus()); - $this->widget->loadDropdownValues($this->db); + $this->widget->loadDropdownValues(); } } \ No newline at end of file diff --git a/lib/HTML/Admin/Widgets/Objects/blockstree.class.inc b/lib/HTML/Admin/Widgets/Objects/blockstree.class.inc index 547dcefb..74a04aa9 100644 --- a/lib/HTML/Admin/Widgets/Objects/blockstree.class.inc +++ b/lib/HTML/Admin/Widgets/Objects/blockstree.class.inc @@ -2,27 +2,22 @@ class PXAdminBlocksTree extends PXAdminTreeObjects { - protected $parentFormat; - protected $parentField; - /** - * - * @param mixed $format - * @param string $fieldName - * @param int $objectId - * @param bool $withoutExternals - */ - public /*override*/ function __construct($format, $fieldName, $objectId, $withoutExternals = false) { + * + * @param mixed $parentFormat + * @param string $parentField + * @param int $objectId + * @param bool $withoutExternals + */ + public /*override*/ function __construct(protected $parentFormat, protected $parentField, $objectId, $withoutExternals = false) { $app = PXRegistry::getApp(); - $this->parentFormat = $format; - $this->parentField = $fieldName; $this->setControlParent($objectId); parent::__construct( $app->types['block'], [ - 'parent_type' => $format->id, - 'parent_field' => $fieldName, + 'parent_type' => $parentFormat->id, + 'parent_field' => $parentField, 'parent_id' => $objectId ], null, diff --git a/lib/HTML/Admin/Widgets/Objects/table.class.inc b/lib/HTML/Admin/Widgets/Objects/table.class.inc index 948fa28e..9b590360 100644 --- a/lib/HTML/Admin/Widgets/Objects/table.class.inc +++ b/lib/HTML/Admin/Widgets/Objects/table.class.inc @@ -57,11 +57,11 @@ class PXAdminTableObjects extends PXAdminObjects ]; $method = 'getObjects'; - if (mb_strlen($this->loadMethod)) { + if (mb_strlen((string) $this->loadMethod)) { $method .= $this->loadMethod; $args[] = $where; - } elseif (mb_strlen($where)) { + } elseif (mb_strlen((string) $where)) { $method .= 'ByWhere'; $args[] = $where; } @@ -117,7 +117,7 @@ class PXAdminTableObjects extends PXAdminObjects $this->widget = new PXAdminTable($this->objects, $this->format, $this->layout->getData); $this->widget->setPosition($this->pager->getPosition()); - $this->widget->loadDropdownValues($this->db); + $this->widget->loadDropdownValues(); $this->_setParentPathname(); } @@ -197,5 +197,3 @@ class PXAdminTableObjects extends PXAdminObjects return parent::html() . $this->pager->html(); } } - -?> diff --git a/lib/HTML/Admin/Widgets/Objects/tree.class.inc b/lib/HTML/Admin/Widgets/Objects/tree.class.inc index 92b053eb..3e0365e4 100644 --- a/lib/HTML/Admin/Widgets/Objects/tree.class.inc +++ b/lib/HTML/Admin/Widgets/Objects/tree.class.inc @@ -2,23 +2,15 @@ class PXAdminTreeObjects extends PXAdminObjects { - protected $withoutExternals; - protected $loadWhere = null; - protected $loadMethod = null; - /** - * - * @param mixed $format - * @param string $where - * @param string $loadMethod - * @param string $withoutExternals - */ - public function __construct($format, $where = null, $loadMethod = null, $withoutExternals = false) + * + * @param mixed $format + * @param string $loadWhere + * @param string $loadMethod + * @param string $withoutExternals + */ + public function __construct($format, protected $loadWhere = null, protected $loadMethod = null, protected $withoutExternals = false) { - $this->loadWhere = $where; - $this->loadMethod = $loadMethod; - $this->withoutExternals = $withoutExternals; - parent::__construct($format); $this->createSubWidget(); @@ -33,17 +25,11 @@ class PXAdminTreeObjects extends PXAdminObjects return; } - switch (strtolower($this->loadMethod)) { - case 'byparent': - $this->tree = $this->db->getObjectsByParent($this->format, null, $this->loadWhere, DB_SELECT_TREE | ($this->withoutExternals ? DB_SELECT_WITHOUT_EXTERNALS : 0)); - break; - case 'byidarray': - $this->tree = $this->db->getObjectsByIdArray($this->format, null, $this->loadWhere, DB_SELECT_TREE | ($this->withoutExternals ? DB_SELECT_WITHOUT_EXTERNALS : 0)); - break; - default: - $this->tree = $this->db->getObjects($this->format, null, DB_SELECT_TREE | ($this->withoutExternals ? DB_SELECT_WITHOUT_EXTERNALS : 0)); - break; - } + $this->tree = match (strtolower((string) $this->loadMethod)) { + 'byparent' => $this->db->getObjectsByParent($this->format, null, $this->loadWhere, DB_SELECT_TREE | ($this->withoutExternals ? DB_SELECT_WITHOUT_EXTERNALS : 0)), + 'byidarray' => $this->db->getObjectsByIdArray($this->format, null, $this->loadWhere, DB_SELECT_TREE | ($this->withoutExternals ? DB_SELECT_WITHOUT_EXTERNALS : 0)), + default => $this->db->getObjects($this->format, null, DB_SELECT_TREE | ($this->withoutExternals ? DB_SELECT_WITHOUT_EXTERNALS : 0)), + }; $this->db->filterByAccess('admin', $this->format, $this->tree); $this->count = sizeof($this->tree->leafs) - 1; @@ -61,7 +47,7 @@ class PXAdminTreeObjects extends PXAdminObjects $this->loadObjects(); $this->widget = new PXAdminTree($this->format, $this->tree); $this->widget->setOpenedLeafs($this->request->getLeafStatus()); - $this->widget->loadDropdownValues($this->db); + $this->widget->loadDropdownValues(); } public function setSelected($selectedSid) @@ -93,4 +79,3 @@ class PXAdminTreeObjects extends PXAdminObjects return $this->tree; } } -?> diff --git a/lib/HTML/Admin/Widgets/abstract.class.inc b/lib/HTML/Admin/Widgets/abstract.class.inc index 901e2972..446da8dc 100644 --- a/lib/HTML/Admin/Widgets/abstract.class.inc +++ b/lib/HTML/Admin/Widgets/abstract.class.inc @@ -5,7 +5,7 @@ interface PXAdminWidgetIF public function html(); } -abstract class PXAdminWidget implements PXAdminWidgetIF +abstract class PXAdminWidget implements PXAdminWidgetIF, \Stringable { public $disable; public $parentWidget = null; @@ -18,9 +18,9 @@ abstract class PXAdminWidget implements PXAdminWidgetIF { } - public function __toString() + public function __toString(): string { - return $this->html(); + return (string) $this->html(); } public function setParent($widget) diff --git a/lib/HTML/Admin/Widgets/ajaxTreeBranch.class.inc b/lib/HTML/Admin/Widgets/ajaxTreeBranch.class.inc index 34ff49c1..29e2ede1 100644 --- a/lib/HTML/Admin/Widgets/ajaxTreeBranch.class.inc +++ b/lib/HTML/Admin/Widgets/ajaxTreeBranch.class.inc @@ -2,7 +2,7 @@ class PXAdminAjaxTreeBranch extends PXAdminTreeBranch { public /* override */ function html() { - $title = strip_tags($this->leaf->title); + $title = strip_tags((string) $this->leaf->title); $html = ''; $html .= 'divAttrs() . '>'; @@ -62,7 +62,7 @@ class PXAdminAjaxTreeBranch extends PXAdminTreeBranch { } public static function childrenHTML($children, &$ajaxHtmlTree, $parentalBranch = null) { - if(!count($children)){ + if(!(is_countable($children) ? count($children) : 0)){ return ''; } @@ -102,4 +102,3 @@ class PXAdminAjaxTreeBranch extends PXAdminTreeBranch { } } -?> diff --git a/lib/HTML/Admin/Widgets/ajaxtree.class.inc b/lib/HTML/Admin/Widgets/ajaxtree.class.inc index 24e7d4b7..7ff80bfd 100644 --- a/lib/HTML/Admin/Widgets/ajaxtree.class.inc +++ b/lib/HTML/Admin/Widgets/ajaxtree.class.inc @@ -3,14 +3,12 @@ require_once PPLIBPATH . 'HTML/Admin/Widgets/ajaxTreeBranch.class.inc'; class PXAdminAjaxTree extends PXAdminTree { - protected $currentLeaf; /** * Конструктор класса PXAdminAjaxTree. * Инициализирует поля класса. необходимые для построения дерева. */ - public function __construct(&$datatype, &$tree, $currentLeafId = null) { + public function __construct(&$datatype, &$tree, protected $currentLeaf = null) { parent::__construct($datatype, $tree); - $this->currentLeaf = $currentLeafId; } public function html() { diff --git a/lib/HTML/Admin/Widgets/form.class.inc b/lib/HTML/Admin/Widgets/form.class.inc index 30728b37..320bb68b 100644 --- a/lib/HTML/Admin/Widgets/form.class.inc +++ b/lib/HTML/Admin/Widgets/form.class.inc @@ -4,8 +4,6 @@ class PXAdminForm extends PXAdminComplexWidget { private $action; private $area; - private $object; - private $format; private $layout; private $childTypes; private $childCount = null; @@ -45,13 +43,10 @@ class PXAdminForm extends PXAdminComplexWidget self::AFTER_CONTENT => [], self::NOTICES_CONTENT => []]; - public function __construct($object, $format) + public function __construct(private $object, private $format) { - $this->object = $object; $this->layout = PXRegistry::getlayout(); $this->app = PXRegistry::getApp(); - - $this->format = $format; } /** @@ -121,13 +116,14 @@ class PXAdminForm extends PXAdminComplexWidget { return NLAbstractHTMLForm::BuildHidden('action', 'edit') . NLAbstractHTMLForm::BuildHidden('area', 'file') . - NLAbstractHTMLForm::BuildHidden('mdir', str_replace(BASEPATH, '', $dir)) . + NLAbstractHTMLForm::BuildHidden('mdir', str_replace(BASEPATH, '', (string) $dir)) . NLAbstractHTMLForm::BuildHidden('mfile', $file) . NLAbstractHTMLForm::BuildText('filesource', ReadFileToString($dir . '/' . $file), '100%'); } public function getForm() { + $html = []; $this->decorate(); $this->layout->setOuterForm('action.phtml', 'POST', 'multipart/form-data'); @@ -139,30 +135,14 @@ class PXAdminForm extends PXAdminComplexWidget $html[] = join($this->blocks[self::BEFORE_CONTENT]); - switch ($this->action) { - case 'main': - $content = $this->mainForm(); - break; - - case 'children': - $content = $this->childrenForm(); - break; - - case 'remove': - $content = $this->removeForm(); - break; - - case 'links': - $content = $this->linksForm(); - break; - - case 'auditlog': - $content = $this->auditlogForm(); - break; - - default: - $content = ""; - } + $content = match ($this->action) { + 'main' => $this->mainForm(), + 'children' => $this->childrenForm(), + 'remove' => $this->removeForm(), + 'links' => $this->linksForm(), + 'auditlog' => $this->auditlogForm(), + default => "", + }; $this->setMenu(); $this->setControls(); @@ -212,7 +192,7 @@ class PXAdminForm extends PXAdminComplexWidget $this->rightControls(); $control = join('', [ - NLAbstractHTMLForm::BuildHidden('id', ((isset($this->object['id'])) ? $this->object['id'] : '')), + NLAbstractHTMLForm::BuildHidden('id', ($this->object['id'] ?? '')), NLAbstractHTMLForm::BuildHidden('area', $this->area), NLAbstractHTMLForm::BuildHidden('action', $this->action), NLAbstractHTMLForm::BuildHidden('format', $this->format->id), @@ -227,7 +207,7 @@ class PXAdminForm extends PXAdminComplexWidget $menuItems['main'] = 'Основная информация'; if (isset($this->object['id']) && $this->object['id'] != 0) { - if (count($this->format->childs)) { + if (is_countable($this->format->childs) ? count($this->format->childs) : 0) { $menuItems['children'] = 'Разрешенные дети'; } @@ -261,7 +241,7 @@ class PXAdminForm extends PXAdminComplexWidget $v = current($group); unset($v['id']); - $viewKlass = sprintf('PXWidgetFieldGroups%sView', ucfirst(mb_strtolower($view = (empty($this->format->groups[$k]['view']) ? $defaultView : $this->format->groups[$k]['view'])))); + $viewKlass = sprintf('PXWidgetFieldGroups%sView', ucfirst(mb_strtolower((string) ($view = (empty($this->format->groups[$k]['view']) ? $defaultView : $this->format->groups[$k]['view']))))); if (!class_exists($viewKlass)) { FatalError("Unknown group view '{$view}' (undefined widget class '{$viewKlass}') in datatype '{$this->format->id}'"); } @@ -271,8 +251,8 @@ class PXAdminForm extends PXAdminComplexWidget if (isset($this->object['sys_created']) || isset($this->object['sys_modified']) || isset($this->object['ownerlogin'])) { $html[] = '
'; - $html[] = isset($this->object['sys_created']) ? '
Создан:
' . mb_substr($this->object['sys_created'], 0, 19) . '
' : ''; - $html[] = isset($this->object['sys_modified']) ? '
Изменен:
' . mb_substr($this->object['sys_modified'], 0, 19) . '
' : ''; + $html[] = isset($this->object['sys_created']) ? '
Создан:
' . mb_substr((string) $this->object['sys_created'], 0, 19) . '
' : ''; + $html[] = isset($this->object['sys_modified']) ? '
Изменен:
' . mb_substr((string) $this->object['sys_modified'], 0, 19) . '
' : ''; $html[] = isset($this->object['ownerlogin']) ? '
Владелец:
' . $this->object['ownerlogin'] . '
' : ''; $html[] = '
'; } @@ -282,9 +262,7 @@ class PXAdminForm extends PXAdminComplexWidget private function removeForm() { - $title = isset($this->object['title']) - ? $this->object['title'] - : ($this->format->id . '#' . $this->object['id']); + $title = $this->object['title'] ?? $this->format->id . '#' . $this->object['id']; return << Вы уверенны, что хотите удалить объект «{$title}» @@ -394,7 +372,7 @@ HTML; $html .= NLAbstractHTMLForm::BuildHidden($reference->name . '[' . $iteration . '][' . $v['id'] . '][id]', $v['id']); $html .= NLAbstractHTMLForm::BuildCheckBox($reference->name . '[' . $iteration . '][' . $v['id'] . '][on]', $linksIsset, NULL, $reference->name . '-' . $iteration . '-' . $v['id']); - $menuTitle = (mb_strlen($v['title']) > 10) ? trim(mb_substr($v['title'], 0, 10)) . '…' : $v['title']; + $menuTitle = (mb_strlen((string) $v['title']) > 10) ? trim(mb_substr((string) $v['title'], 0, 10)) . '…' : $v['title']; $context = 'Context(event, \'edit\', ' . $v['id'] . ', ' . (int)($v['status']) . ', \'' . $formatTo->id . '\', \'' . $menuTitle . '\', \'\', 1, 0, 0); return false;'; $html .= ''; $html .= 'Изменить «' . $v['id'] . '»'; @@ -509,7 +487,8 @@ HTML; private function childrenForm() { - $values = [ + $html = []; + $values = [ PP_CHILDREN_FETCH_ALL => 'Все', PP_CHILDREN_FETCH_SELECTED => 'Текущий', PP_CHILDREN_FETCH_NONE => 'Никакие', @@ -532,7 +511,7 @@ HTML; $html[] = '
' . "\n"; foreach ($this->childTypes as $k => $v) { - $quantity = (isset($this->childCount[$k])) ? $this->childCount[$k] : 0; + $quantity = $this->childCount[$k] ?? 0; if (!in_array($k, $this->format->childs)) { continue; diff --git a/lib/HTML/Admin/Widgets/list.class.inc b/lib/HTML/Admin/Widgets/list.class.inc index 5b651393..dd5a3127 100644 --- a/lib/HTML/Admin/Widgets/list.class.inc +++ b/lib/HTML/Admin/Widgets/list.class.inc @@ -4,14 +4,12 @@ use \PP\Lib\Html\Layout\LayoutAbstract; class PXAdminList extends PXAdminWidget { - public $_list; public $_varName; public $_selected; public $_getData; - public function __construct($list) + public function __construct(public $_list) { - $this->_list = $list; $this->_varName = ''; $this->_selected = ''; $this->_getData = []; diff --git a/lib/HTML/Admin/Widgets/menuTabbed.class.inc b/lib/HTML/Admin/Widgets/menuTabbed.class.inc index aa063eb5..555bd72e 100644 --- a/lib/HTML/Admin/Widgets/menuTabbed.class.inc +++ b/lib/HTML/Admin/Widgets/menuTabbed.class.inc @@ -4,9 +4,9 @@ require_once PPLIBPATH . 'HTML/Admin/Widgets/tabs.class.inc'; class PXWidgetMenuTabbed extends PXAdminComplexWidget { - public const BEFORE_TABS = 0; - public const TABS = 1; - public const AFTER_TABS = 2; + final public const BEFORE_TABS = 0; + final public const TABS = 1; + final public const AFTER_TABS = 2; public $items; public $varName = 'area'; diff --git a/lib/HTML/Admin/Widgets/pager.class.inc b/lib/HTML/Admin/Widgets/pager.class.inc index 9e50436c..7fc2a81d 100644 --- a/lib/HTML/Admin/Widgets/pager.class.inc +++ b/lib/HTML/Admin/Widgets/pager.class.inc @@ -6,10 +6,7 @@ use \PP\Lib\Html\Layout\LayoutAbstract; class PXAdminPager extends PXAdminComplexWidget { public $_currentPage; - public $_rowsPerPage; - public $_rowsCount; public $_pagerName; - public $_getData; public const BEFORE_PAGE_LIST = 0; public const AFTER_PAGE_LIST = 1; @@ -20,25 +17,22 @@ class PXAdminPager extends PXAdminComplexWidget ]; /** - * Конструктор класса PXAdminPager. - * Инициализирует поля класса. необходимые для построения постраничной навигации. - * @param Integer $cur - текущая страница, нумерация начинается с 1 ! - * @param Integer $rows - по сколько записей выводить на страницу - * @param Integer $count - общее количество записей - * @param PXTypeDescription|string $pager_name - имя переменной для постраничной навигации, конкатенируется с '_page' - * @param array $getData - массив с GET переменными, которые надо добавлять в ссылки постраничной навигации, обычно берётся из поля $layout->getData - */ - public function __construct($cur, $rows = 1, $count = 1, $pager_name = '', $get_data = null) + * Конструктор класса PXAdminPager. + * Инициализирует поля класса. необходимые для построения постраничной навигации. + * @param Integer $cur - текущая страница, нумерация начинается с 1 ! + * @param Integer $_rowsPerPage - по сколько записей выводить на страницу + * @param Integer $_rowsCount - общее количество записей + * @param PXTypeDescription|string $pager_name - имя переменной для постраничной навигации, конкатенируется с '_page' + * @param array $getData - массив с GET переменными, которые надо добавлять в ссылки постраничной навигации, обычно берётся из поля $layout->getData + */ + public function __construct($cur, public $_rowsPerPage = 1, public $_rowsCount = 1, $pager_name = '', public $_getData = null) { if ($pager_name instanceof PXTypeDescription) { $pager_name = $pager_name->id; } $this->_currentPage = (int)$cur > 0 ? $cur : '1'; - $this->_rowsPerPage = $rows; - $this->_rowsCount = $count; $this->_pagerName = $pager_name; - $this->_getData = $get_data; } public function html() @@ -143,11 +137,11 @@ class PXAdminPager extends PXAdminComplexWidget public static function getPageByFormatId($formatId, $default = null) { - return PXRegistry::getRequest()->getGetVar(self::getPageParamByFormatId($formatId), $default); + return PXRegistry::getRequest()->getGetVar(self::getPageParamByFormatId($formatId)); } public static function getPerPageByFormatId($formatId, $default = null) { - return PXRegistry::getRequest()->getGetVar(self::getPerPageParamByFormatId($formatId), $default); + return PXRegistry::getRequest()->getGetVar(self::getPerPageParamByFormatId($formatId)); } } diff --git a/lib/HTML/Admin/Widgets/table.class.inc b/lib/HTML/Admin/Widgets/table.class.inc index ed33148f..bfc2a77b 100644 --- a/lib/HTML/Admin/Widgets/table.class.inc +++ b/lib/HTML/Admin/Widgets/table.class.inc @@ -15,8 +15,6 @@ use PP\Lib\Html\Layout\LayoutAbstract; class PXAdminTable extends PXAdminComplexWidget { - public $data; - public $dtype; public $getData; public $titleKey; @@ -48,15 +46,13 @@ class PXAdminTable extends PXAdminComplexWidget * @param Array $getData - массив с GET переменными которые надо добавлять в ссылки таблицы * @param String $titleKey - тип поля в datatype по которому будет определяться 'title' таблицы */ - public function __construct($data, $dtype, $getData, $titleKey = NULL) + public function __construct(public $data, public $dtype, $getData, $titleKey = NULL) { - $this->data = $data; - $this->dtype = $dtype; $this->getData = !empty($getData) ? $getData : []; - $this->currentOrder = isset($getData[$this->dtype->id . '_order']) ? $getData[$this->dtype->id . '_order'] : $this->dtype->order; + $this->currentOrder = $getData[$this->dtype->id . '_order'] ?? $this->dtype->order; - $this->titleKey = ($titleKey) ? $titleKey : 'title'; + $this->titleKey = $titleKey ?: 'title'; $this->controls = []; $this->showDefaultControls = 'before'; @@ -135,7 +131,7 @@ class PXAdminTable extends PXAdminComplexWidget } } - if (count($this->data)) { + if (is_countable($this->data) ? count($this->data) : 0) { $html .= join($this->blocks[self::BEFORE_CONTENT]); $html .= ''; @@ -143,7 +139,7 @@ class PXAdminTable extends PXAdminComplexWidget //}s'.REGEX_MOD, 'yyyTypo', $text); + $text = preg_replace_callback('{}s'.REGEX_MOD, 'yyyTypo', (string) $text); $PrivateTags = "title|script|style|pre|textarea"; $text = preg_replace_callback('{<\s*('.$PrivateTags.')[\s>].*?<\s*/\s*\1\s*>}is'.REGEX_MOD, 'yyyTypo', $text); @@ -99,7 +99,7 @@ function TypoAll($text, $isHTML = true) { ещё одной кавычки */ $prequote = '\s\(\[\{";-'; - $text = preg_replace('{^"}'.REGEX_MOD, LAQUO, $text); + $text = preg_replace('{^"}'.REGEX_MOD, LAQUO, (string) $text); $text = preg_replace('{(?<=['.$prequote.'])"}'.REGEX_MOD, LAQUO, $text); // а это для тех, кто нарушает ВЕЛИКОЕ ПРАВИЛО @@ -172,7 +172,7 @@ function UnTypoAll($text) { $text = str_replace( ['«','»','„','“','”','—','–','…','’', '№','“','”'], ['"', '"', '"', '"', '"', '-', '-', '...', "'", NUMBER, '"', '"'], - $text + (string) $text ); return $text; @@ -188,4 +188,3 @@ function UnTypoAllRecursive($mixed) { } return $mixed; } -?> diff --git a/lib/Triggers/db.class.inc b/lib/Triggers/db.class.inc index cdd4c5b5..d97ce359 100644 --- a/lib/Triggers/db.class.inc +++ b/lib/Triggers/db.class.inc @@ -13,14 +13,13 @@ abstract class PXAbstractDatabaseTrigger extends PXAbstractTrigger } /** - * @param PXDatabase|PostgreSqlDriver $db - * @param int $objId - * @param PXTypeDescription $format - * @param array $object - * @param array $dbFields - * @param array $dbValues - */ - public function OnAddObject(&$db, &$objId, &$format, &$object, &$dbFields, &$dbValues) + * @param int $objId + * @param PXTypeDescription $format + * @param array $object + * @param array $dbFields + * @param array $dbValues + */ + public function OnAddObject(\PXDatabase|\PP\Lib\Database\Driver\PostgreSqlDriver &$db, &$objId, &$format, &$object, &$dbFields, &$dbValues) { } @@ -30,15 +29,14 @@ abstract class PXAbstractDatabaseTrigger extends PXAbstractTrigger } /** - * @param PXDatabase|PostgreSqlDriver $db - * @param int $objId - * @param PXTypeDescription $format - * @param array $object - * @param array $dbFields - * @param array $dbValues - * @param array $objectInDB - */ - public function OnModifyObject(&$db, &$objId, &$format, &$object, &$dbFields, &$dbValues, &$objectInDB) + * @param int $objId + * @param PXTypeDescription $format + * @param array $object + * @param array $dbFields + * @param array $dbValues + * @param array $objectInDB + */ + public function OnModifyObject(\PXDatabase|\PP\Lib\Database\Driver\PostgreSqlDriver &$db, &$objId, &$format, &$object, &$dbFields, &$dbValues, &$objectInDB) { } diff --git a/lib/Triggers/system.class.inc b/lib/Triggers/system.class.inc index c52469ce..ab8e01db 100644 --- a/lib/Triggers/system.class.inc +++ b/lib/Triggers/system.class.inc @@ -28,7 +28,7 @@ abstract class PXAbstractSystemTrigger extends PXAbstractTrigger { * @param PXModuleDescription $moduleDescription the module * @param mixed $eventData Info about the engine method to be called */ - public function onBeforeModuleRun($currentEngine, $moduleDescription, $eventData) { + public function onBeforeModuleRun($currentEngine, $moduleDescription, mixed $eventData) { } /** @@ -38,7 +38,7 @@ abstract class PXAbstractSystemTrigger extends PXAbstractTrigger { * @param PXModuleDescription $moduleDescription the module * @param mixed $eventData Info about the called engine method */ - public function onAfterModuleRun($currentEngine, $moduleDescription, $eventData) { + public function onAfterModuleRun($currentEngine, $moduleDescription, mixed $eventData) { } } diff --git a/lib/User/abstract.class.inc b/lib/User/abstract.class.inc index 6259bcb5..fc03f846 100644 --- a/lib/User/abstract.class.inc +++ b/lib/User/abstract.class.inc @@ -2,7 +2,7 @@ define('USERLEVEL_UNAUTHED', 0); define('USERLEVEL_USER', 1024); define('USERLEVEL_ADMIN', 8192); -define('USER_SESSION_INTERVAL', time() + 2592000); +define('USER_SESSION_INTERVAL', time() + 2_592_000); define('DT_USER', 'suser'); define('DT_GROUP', 'sgroup'); @@ -47,10 +47,9 @@ abstract class PXUser } /** - * @param PXDatabase $db - * @return $this - */ - public function setDb(PXDatabase $db) + * @return $this + */ + public function setDb(PXDatabase $db) { $this->db = $db; @@ -61,10 +60,9 @@ abstract class PXUser } /** - * @param PXApplication $app - * @return $this - */ - public function setApp(PXApplication $app) + * @return $this + */ + public function setApp(PXApplication $app) { $this->app = $app; @@ -72,10 +70,9 @@ abstract class PXUser } /** - * @param PXRequest $request - * @return $this - */ - public function setRequest(PXRequest $request) + * @return $this + */ + public function setRequest(PXRequest $request) { $this->request = $request; diff --git a/lib/User/auth.class.inc b/lib/User/auth.class.inc index 44c23370..493aeb2e 100644 --- a/lib/User/auth.class.inc +++ b/lib/User/auth.class.inc @@ -130,7 +130,7 @@ class PXUserAuthorized extends PXUser public function aclType() { - return (count($this->groups) > 1) ? 'bygroup' : 'basic'; + return ((is_countable($this->groups) ? count($this->groups) : 0) > 1) ? 'bygroup' : 'basic'; } } diff --git a/lib/XML/XSLT/abstract.class.inc b/lib/XML/XSLT/abstract.class.inc index 998c7998..7caaccfc 100644 --- a/lib/XML/XSLT/abstract.class.inc +++ b/lib/XML/XSLT/abstract.class.inc @@ -2,12 +2,10 @@ // abstract class PXXSLT { - public $xsl; public $parameters = []; - public function __construct($xslFile) + public function __construct(public $xsl) { - $this->xsl = $xslFile; } public function addParameter($key, $value) diff --git a/lib/XML/XSLT/libxslt.class.inc b/lib/XML/XSLT/libxslt.class.inc index cb683b8f..621c4db4 100644 --- a/lib/XML/XSLT/libxslt.class.inc +++ b/lib/XML/XSLT/libxslt.class.inc @@ -71,7 +71,7 @@ class PXLibXSLT extends PXXSLT '%C3%91' => '%FF' // я ]; - $result = utf8_decode($result); + $result = mb_convert_encoding((string) $result, 'ISO-8859-1'); $result = strtr($result, array_flip(get_html_translation_table(HTML_ENTITIES))); return strtr($result, $rus_chars); @@ -89,7 +89,7 @@ class PXLibXSLT extends PXXSLT $xsl->importStyleSheet($xslf); foreach ($this->parameters as $k => $v) { - $xsl->setParameter('', $k, utf8_encode($v)); + $xsl->setParameter('', $k, mb_convert_encoding((string) $v, 'UTF-8', 'ISO-8859-1')); } return $xsl->transformToXML($xml); @@ -106,5 +106,3 @@ class PXLibXSLT extends PXXSLT return $html; } } - -?> diff --git a/lib/common.defines.inc b/lib/common.defines.inc index 087800a7..b778a277 100644 --- a/lib/common.defines.inc +++ b/lib/common.defines.inc @@ -36,12 +36,12 @@ if (!defined('IS_CLI')) { if (IS_CLI || empty($_SERVER['DOCUMENT_ROOT'])) { // TODO: this will be wrong in case of vendor/pp/core - $_SERVER['DOCUMENT_ROOT'] = realpath(dirname(__FILE__) . '/../../'); + $_SERVER['DOCUMENT_ROOT'] = realpath(__DIR__ . '/../'); } // Определение BASEPATH - это полный путь к корню сайта if (!defined('BASEPATH')) { - $basePath = str_replace("/", DIRECTORY_SEPARATOR, $_SERVER["DOCUMENT_ROOT"]); + $basePath = str_replace("/", DIRECTORY_SEPARATOR, (string) $_SERVER["DOCUMENT_ROOT"]); $basePath = rtrim($basePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; define('BASEPATH', $basePath); } @@ -84,8 +84,8 @@ if (file_exists(SHAREDPATH . '/etc/php.ini')) { ini_set($key, $param); } - $systemUser = (isset($params['user'])) ? $params['user'] : $systemUser; - $systemEnv = (isset($params['env'])) ? $params['env'] : $systemEnv; + $systemUser = $params['user'] ?? $systemUser; + $systemEnv = $params['env'] ?? $systemEnv; } define('SYSTEM_USER', $systemUser); diff --git a/lib/common.version.inc b/lib/common.version.inc index 08ff8616..51986c10 100644 --- a/lib/common.version.inc +++ b/lib/common.version.inc @@ -1,3 +1,3 @@ load_map[mb_strtolower($class)]; + return @self::getInstance()->load_map[mb_strtolower((string) $class)]; } public function setPath($path) { - $this->path = mb_strlen($path) ? $path : $this->path; + $this->path = mb_strlen((string) $path) ? $path : $this->path; return $this; } public function load($class, $path = null) { - $this->load_map[mb_strtolower($class)] = + $this->load_map[mb_strtolower((string) $class)] = realpath(sprintf("%s/%s", $this->path, $path)); return $this; } } - -?> diff --git a/lib/logger.class.inc b/lib/logger.class.inc index 815c38de..c3410029 100644 --- a/lib/logger.class.inc +++ b/lib/logger.class.inc @@ -6,18 +6,6 @@ // TODO: refactor to use monolog class PXAuditLogger { - /** - * Ссылка на PXApplication - * @var PXApplication - */ - public $app; - - /** - * Ссылка на PXDatabase - * @var PXDatabase - */ - public $db; - /** * Ссылка на NLLogger * @var NLLogger @@ -35,11 +23,8 @@ class PXAuditLogger * @param PXApplication $app ссылка на PXApplication {@link $app} * @param PXDatabase $db ссылка на PXDatabase {@link $db} */ - public function __construct($app, $db) + public function __construct(public $app, public $db) { - $this->app = $app; - $this->db = $db; - $this->logger = NLLogger::getLogger('audit'); $loggerSqlFormatFields = [ diff --git a/lib/mainadmin.inc b/lib/mainadmin.inc index bf05bbf6..d4849d51 100644 --- a/lib/mainadmin.inc +++ b/lib/mainadmin.inc @@ -1,6 +1,6 @@ userRegistry = new PXRegistryUSER(); @@ -105,10 +105,9 @@ final class PXRegistry { } /** - * @static - * @return PXDatabase|PostgreSqlDriver - */ - public static function getDb() { + * @static + */ + public static function getDb(): \PXDatabase|\PP\Lib\Database\Driver\PostgreSqlDriver { return self::get('db'); } @@ -129,7 +128,6 @@ final class PXRegistry { } /** - * @static * @return Layout\NullLayout|PXUserHTMLLayout */ public static function getLayout() { @@ -173,7 +171,7 @@ final class PXRegistry { } if(!is_null($typeName)) { - return isset($types[$typeName]) ? $types[$typeName] : null; + return $types[$typeName] ?? null; } return $types; @@ -184,7 +182,7 @@ final class PXRegistry { } private static function get_registry_fields() { - return array_keys(get_class_vars(__CLASS__)); + return array_keys(get_class_vars(self::class)); } public static function assignToObject($object) { @@ -195,7 +193,7 @@ final class PXRegistry { if(empty($fields)) return; foreach($fields as $field) { - $registry_method = "get".ucfirst($field); + $registry_method = "get".ucfirst((string) $field); $object->$field = self::$registry_method(); } } @@ -241,7 +239,7 @@ final class PXRegistry { final class PXRegistryUSER { - private $__hash__; + private ?array $__hash__ = null; public function get($key) diff --git a/lib/search.class.inc b/lib/search.class.inc index 26fbff7a..5a4d37a5 100644 --- a/lib/search.class.inc +++ b/lib/search.class.inc @@ -8,7 +8,7 @@ function SortByRelevant($a, $b) { } function _yo2ye($s) { - return str_replace('Ё', 'Е', str_replace('ё', 'е', $s)); + return str_replace('Ё', 'Е', str_replace('ё', 'е', (string) $s)); } class PXSearch @@ -24,13 +24,10 @@ class PXSearch public $dbah; - public $withIndexField; - - public function __construct($withIndexField = true) + public function __construct(public $withIndexField = true) { $this->app = PXRegistry::getApp(); $this->db = PXRegistry::getDB(); - $this->withIndexField = $withIndexField; $this->searchCount = 0; $this->loadStopwords( @@ -58,7 +55,7 @@ class PXSearch $this->parents = $parents; $stemsArray = $this->_getStemsArray($query); - if (!count($stemsArray)) { + if (!(is_countable($stemsArray) ? count($stemsArray) : 0)) { return []; } @@ -83,7 +80,7 @@ class PXSearch public function loadStopwords($s) { - preg_match_all("/\w+/s" . REGEX_MOD, $s, $m); + preg_match_all("/\w+/s" . REGEX_MOD, (string) $s, $m); $this->stopwords = []; foreach ($m[0] as $v) { $this->stopwords[$v] = true; @@ -116,7 +113,7 @@ class PXSearch public function getStemByWord($word) { - if (mb_strlen($word) <= 2 || mb_strlen($word) > 30) { + if (mb_strlen((string) $word) <= 2 || mb_strlen((string) $word) > 30) { return NULL; } @@ -161,7 +158,7 @@ class PXSearch public static function strip($s) { - $s = preg_replace("//is" . REGEX_MOD, '', $s); // + $s = preg_replace("//is" . REGEX_MOD, '', (string) $s); // $s = preg_replace("/<.*?>/s" . REGEX_MOD, ' ', $s); $s = preg_replace("/ /is" . REGEX_MOD, ' ', $s); $s = html_entity_decode($s, ENT_QUOTES, DEFAULT_CHARSET); @@ -178,12 +175,12 @@ class PXSearch function _getStemsSQL($stemsArray, $allowTypes) { $where = [ - "stem IN (" . join(',', array_map([$this, 'escapeString'], $stemsArray)) . ")", + "stem IN (" . join(',', array_map($this->escapeString(...), $stemsArray)) . ")", ]; if (isset($allowTypes) && is_array($allowTypes)) { $allowTypes = array_flip($allowTypes); - $where[] = "dtype IN (" . join(',', array_map([$this, 'escapeString'], $allowTypes)) . ")"; + $where[] = "dtype IN (" . join(',', array_map($this->escapeString(...), $allowTypes)) . ")"; } foreach (PXRegistry::getApp()->triggers->search as $t) { @@ -191,7 +188,7 @@ class PXSearch } $where = implode(' AND ', $where); - $totalwords = count($stemsArray); + $totalwords = is_countable($stemsArray) ? count($stemsArray) : 0; return <<getStemsByArray(array_unique($tmp[0]))); } @@ -295,7 +292,7 @@ SQL; } foreach ($dtypes as $k => $v) { - if (!count($v)) { + if (!(is_countable($v) ? count($v) : 0)) { continue; } @@ -347,4 +344,3 @@ SQL; return $this->searchCount; } } -?> diff --git a/lib/smarty.plugins/compiler.break.php b/lib/smarty.plugins/compiler.break.php index 15a5c776..c296b312 100644 --- a/lib/smarty.plugins/compiler.break.php +++ b/lib/smarty.plugins/compiler.break.php @@ -10,6 +10,6 @@ */ function smarty_compiler_break($level, &$smarty) { - !ctype_digit($level) && $level = ''; + !ctype_digit((string) $level) && $level = ''; return "\nbreak $level;"; } diff --git a/lib/smarty.plugins/compiler.continue.php b/lib/smarty.plugins/compiler.continue.php index 5ca5daed..af338525 100644 --- a/lib/smarty.plugins/compiler.continue.php +++ b/lib/smarty.plugins/compiler.continue.php @@ -10,6 +10,6 @@ */ function smarty_compiler_continue($level, &$smarty) { - !ctype_digit($level) && $level = ''; + !ctype_digit((string) $level) && $level = ''; return "\ncontinue $level;"; } diff --git a/lib/smarty.plugins/modifier.date_to_time.php b/lib/smarty.plugins/modifier.date_to_time.php index f733f5c8..1fda49eb 100644 --- a/lib/smarty.plugins/modifier.date_to_time.php +++ b/lib/smarty.plugins/modifier.date_to_time.php @@ -8,7 +8,7 @@ function smarty_modifier_date_to_time($string) { return mktime(0,0,0,date('n'),1); } elseif ($string != '') { - preg_match("/^(\d{2})\.(\d{2})\.(\d{4})\s+(\d{2}):(\d{2}):(\d{2})$/si".REGEX_MOD, trim($string), $date); + preg_match("/^(\d{2})\.(\d{2})\.(\d{4})\s+(\d{2}):(\d{2}):(\d{2})$/si".REGEX_MOD, trim((string) $string), $date); return mktime($date[4], $date[5], $date[6], $date[2], $date[1], $date[3]); @@ -16,5 +16,3 @@ function smarty_modifier_date_to_time($string) { return time(); } } - -?> diff --git a/plugins/multipleregions/lib/cloner.class.inc b/plugins/multipleregions/lib/cloner.class.inc index 1a41c83c..233c8c6c 100644 --- a/plugins/multipleregions/lib/cloner.class.inc +++ b/plugins/multipleregions/lib/cloner.class.inc @@ -1,4 +1,4 @@ -getItem(PXApplication::class); - if ($cachedApplication = $cache->get(PXApplication::class)) { - $paths = $cachedApplication->getConfigurationPaths(); - $created = $cachedApplication->getCreated(); - $finder = new Finder(); - $finder->files() - ->ignoreUnreadableDirs()->ignoreDotFiles(false) - ->name('*.{yml,yaml,xml,ini}')->name('.env') - ->depth('== 0') - ->date('>= @' . $created) - ->in(BASEPATH)->in($paths); + if ($applicationCacheItem->isHit()) { + $cachedApplication = $applicationCacheItem->get(); - if (count($finder) === 0) { - return $cachedApplication; - } - } + $paths = $cachedApplication->getConfigurationPaths(); + $created = $cachedApplication->getCreated(); - $application = new PXApplication(); - $application->init(); + $finder = new Finder(); + $finder->files() + ->ignoreUnreadableDirs()->ignoreDotFiles(false) + ->name('*.{yml,yaml,xml,ini}')->name('.env') + ->depth('== 0') + ->date('>= @' . $created) + ->in(BASEPATH)->in($paths); - $cache->set(PXApplication::class, $application); + if (count($finder) === 0) { + return $cachedApplication; + } + } - return $application; - } + $application = new PXApplication(); + $application->init(); + + $applicationCacheItem->set($application); + $cache->save($applicationCacheItem); + + return $application; + } } diff --git a/src/Command/CompileContainerCommand.php b/src/Command/CompileContainerCommand.php index 5e245013..c5c568c9 100644 --- a/src/Command/CompileContainerCommand.php +++ b/src/Command/CompileContainerCommand.php @@ -16,38 +16,40 @@ * * @package PP\Command */ -class CompileContainerCommand extends AbstractCommand { - - /** - * {@inheritdoc} - */ - protected function configure() { - $this - ->setName('pp:dump:container') - ->setDescription('Compiles container to static php file'); - } - - /** - * {@inheritdoc} - * - * @throws \Exception - */ - public function execute(InputInterface $input, OutputInterface $output) { - $file = CACHE_PATH . DIRECTORY_SEPARATOR . 'container.php'; - $containerConfigCache = new ConfigCache($file, false); - - $path = APPPATH . 'config'; - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator($path)); - - $loader->load('services.yml'); - $container->compile(true); - - $dumper = new PhpDumper($container); - $containerConfigCache->write( - $dumper->dump(['class' => 'MyCachedContainer']), - $container->getResources() - ); - } +class CompileContainerCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('pp:dump:container') + ->setDescription('Compiles container to static php file'); + } + + /** + * {@inheritdoc} + * + * @throws \Exception + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $file = CACHE_PATH . DIRECTORY_SEPARATOR . 'container.php'; + $containerConfigCache = new ConfigCache($file, false); + + $path = APPPATH . 'config'; + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator($path)); + + $loader->load('services.yml'); + $container->compile(true); + + $dumper = new PhpDumper($container); + $containerConfigCache->write( + $dumper->dump(['class' => 'MyCachedContainer']), + $container->getResources() + ); + } } diff --git a/src/Command/CronCommand.php b/src/Command/CronCommand.php index c3bcd7a5..6bae464b 100644 --- a/src/Command/CronCommand.php +++ b/src/Command/CronCommand.php @@ -15,87 +15,89 @@ * Class CronCommand * @package PP\Command */ -class CronCommand extends AbstractCommand { - - /** - * {@inheritdoc} - */ - protected function configure() { - $this - ->setName('cron') - ->setDescription('Run scheduled cron jobs') - ->addOption('list', 'l', InputOption::VALUE_NONE, 'display list of cronruns') - ->addArgument('task', InputArgument::OPTIONAL, 'task name to run'); - } - - /** - * {@inheritdoc} - */ - public function execute(InputInterface $input, OutputInterface $output) { - if (!isset($this->app->modules['cronrun'])) { - return; - } - - /** @var \PP\Module\CronRunModule $cronModule */ - $cronModule = $this->app->modules['cronrun']->getModule(); - - if ($cronModule instanceof ContainerAwareInterface) { - $cronModule->setContainer($this->container); - } - - $listTasks = $input->getOption('list'); - $task = $input->getArgument('task'); - - if ($listTasks === true) { - $output->writeln(''.str_repeat("-", 132).''); - $header = [ - mb_str_pad('Название задачи', 25), - mb_str_pad('Расписание', 15), - mb_str_pad('Описание задачи', 40), - mb_str_pad('Дата запуска', 21), - mb_str_pad('Дата завершения', 21), - ]; - - $output->writeln('' . join(' | ', $header) . ''); - $output->writeln('' . str_repeat('-', 132) . ''); - - foreach ($cronModule->jobs as $task => $j) { - $stat = $cronModule->getStat($j); - - $title = mb_str_pad($task, 25); - $title = (mb_strlen($title) > 25) - ? mb_substr($title, 0, 22) . '...' - : $title; - - $description = mb_str_pad($j['job']->name, 40); - $description = (mb_strlen($description) > 40) - ? mb_substr($description, 0, 37).'...' - : $description; - - $row = [ - '' . $title . '', - mb_str_pad($j['rule']->asString, 15), - $description, - mb_str_pad(strftime("%Y-%m-%d %H:%M:%S", $stat['start']), 21), - mb_str_pad(strftime("%Y-%m-%d %H:%M:%S", $stat['end']), 21), - ]; - - $output->writeln(implode(' | ', $row)); - } - - $output->writeln(''); - - } elseif ($task !== null) { - if (!isset($cronModule->jobs[$task])) { - throw new \InvalidArgumentException("Unknown task: {$task}"); - } - - $output->writeln('Starting requested job: ' . $task); - $cronModule->runJob($cronModule->jobs[$task], $this->app, time()); - - } else { - $output->writeln('Starting scheduled jobs..'); - $cronModule->RunTasks($this->app, time()); - } - } +class CronCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('cron') + ->setDescription('Run scheduled cron jobs') + ->addOption('list', 'l', InputOption::VALUE_NONE, 'display list of cronruns') + ->addArgument('task', InputArgument::OPTIONAL, 'task name to run'); + } + + /** + * {@inheritdoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + if (!isset($this->app->modules['cronrun'])) { + return; + } + + /** @var \PP\Module\CronRunModule $cronModule */ + $cronModule = $this->app->modules['cronrun']->getModule(); + + if ($cronModule instanceof ContainerAwareInterface) { + $cronModule->setContainer($this->container); + } + + $listTasks = $input->getOption('list'); + $task = $input->getArgument('task'); + + if ($listTasks === true) { + $output->writeln(''.str_repeat("-", 132).''); + $header = [ + mb_str_pad('Название задачи', 25), + mb_str_pad('Расписание', 15), + mb_str_pad('Описание задачи', 40), + mb_str_pad('Дата запуска', 21), + mb_str_pad('Дата завершения', 21), + ]; + + $output->writeln('' . join(' | ', $header) . ''); + $output->writeln('' . str_repeat('-', 132) . ''); + + foreach ($cronModule->jobs as $task => $j) { + $stat = $cronModule->getStat($j); + + $title = mb_str_pad($task, 25); + $title = (mb_strlen((string) $title) > 25) + ? mb_substr((string) $title, 0, 22) . '...' + : $title; + + $description = mb_str_pad($j['job']->name, 40); + $description = (mb_strlen((string) $description) > 40) + ? mb_substr((string) $description, 0, 37).'...' + : $description; + + $row = [ + '' . $title . '', + mb_str_pad($j['rule']->asString, 15), + $description, + mb_str_pad(strftime("%Y-%m-%d %H:%M:%S", $stat['start']), 21), + mb_str_pad(strftime("%Y-%m-%d %H:%M:%S", $stat['end']), 21), + ]; + + $output->writeln(implode(' | ', $row)); + } + + $output->writeln(''); + + } elseif ($task !== null) { + if (!isset($cronModule->jobs[$task])) { + throw new \InvalidArgumentException("Unknown task: {$task}"); + } + + $output->writeln('Starting requested job: ' . $task); + $cronModule->runJob($cronModule->jobs[$task], $this->app, time()); + + } else { + $output->writeln('Starting scheduled jobs..'); + $cronModule->RunTasks($this->app, time()); + } + } } diff --git a/src/Command/FillMetaCommand.php b/src/Command/FillMetaCommand.php index e69f5f2b..2988c899 100644 --- a/src/Command/FillMetaCommand.php +++ b/src/Command/FillMetaCommand.php @@ -10,75 +10,77 @@ * Class CreateMetaCommand * @package PP\Command */ -class FillMetaCommand extends AbstractCommand { +class FillMetaCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('db:fill:meta') + ->setDescription('Fill all empty sys_meta field'); + } - /** - * {@inheritdoc} - */ - protected function configure() { - $this - ->setName('db:fill:meta') - ->setDescription('Fill all empty sys_meta field'); - } + /** + * {@inheritdoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $limit = 100; - /** - * {@inheritdoc} - */ - public function execute(InputInterface $input, OutputInterface $output) { - $limit = 100; + foreach ($this->app->types as $type) { + $needProcess = false; + foreach ($type->fields as $v) { + if (!$v->storageType->storedInDb()) { + $needProcess = true; + break; + } + } - foreach ($this->app->types as $type) { - $needProcess = false; - foreach ($type->fields as $v) { - if (!$v->storageType->storedInDb()) { - $needProcess = true; - break; - } - } + if (!$needProcess) { + $output->writeln(sprintf("No need to be processed: %s", $type->id)); + continue; + } - if (!$needProcess) { - $output->writeln(sprintf("No need to be processed: %s", $type->id)); - continue; - } + // @TODO: refactor, to check sys_meta = NULL + $output->writeln(sprintf("Processing: %s", $type->id)); + $queryUpdateFmt = 'UPDATE %s SET %s WHERE id = %s'; + $querySelectFmt = 'SELECT * FROM %s WHERE id > %d ORDER BY id ASC LIMIT %d'; + $lastId = 0; - // @TODO: refactor, to check sys_meta = NULL - $output->writeln(sprintf("Processing: %s", $type->id)); - $queryUpdateFmt = 'UPDATE %s SET %s WHERE id = %s'; - $querySelectFmt = 'SELECT * FROM %s WHERE id > %d ORDER BY id ASC LIMIT %d'; - $lastId = 0; + while (true) { + $selector = sprintf($querySelectFmt, $type->id, $lastId, $limit); + $objectList = $this->db->Query($selector); + if (empty($objectList)) { + break; + } - while (true) { - $selector = sprintf($querySelectFmt, $type->id, $lastId, $limit); - $objectList = $this->db->Query($selector); - if (empty($objectList)) { - break; - } + $this->db->_NormalizeTable($objectList, $type, false); + foreach ($objectList as $object) { - $this->db->_NormalizeTable($objectList, $type, false); - foreach ($objectList as $object) { + $sysMetaField = []; + foreach ($type->fields as $k => $v) { + if (!$v->storageType->storedInDb()) { + $p = ['id' => $object['id'], 'format' => $type->id]; + if (($proceedFileResult = $v->storageType->proceedFile($v, $object, $p))) { + $sysMetaField[$k] = $proceedFileResult; + } + } + } - $sysMetaField = []; - foreach ($type->fields as $k => $v) { - if (!$v->storageType->storedInDb()) { - $p = ['id' => $object['id'], 'format' => $type->id]; - if (($proceedFileResult = $v->storageType->proceedFile($v, $object, $p))) { - $sysMetaField[$k] = $proceedFileResult; - } - } - } + $metaField = (count($sysMetaField) > 0) + ? $this->db->MapData(json_encode($sysMetaField)) + : 'NULL'; - $metaField = (count($sysMetaField) > 0) - ? $this->db->MapData(json_encode($sysMetaField)) - : 'NULL'; + $metaField = sprintf("%s = %s", OBJ_FIELD_META, $metaField); - $metaField = sprintf("%s = %s", OBJ_FIELD_META, $metaField); - - // fire! - $lastId = $object['id']; - $query = sprintf($queryUpdateFmt, $type->id, $metaField, $lastId); - $this->db->query($query); - } - } - } - } + // fire! + $lastId = $object['id']; + $query = sprintf($queryUpdateFmt, $type->id, $metaField, $lastId); + $this->db->query($query); + } + } + } + } } diff --git a/src/Command/FillUuidCommand.php b/src/Command/FillUuidCommand.php index 7f757422..4b9948b4 100644 --- a/src/Command/FillUuidCommand.php +++ b/src/Command/FillUuidCommand.php @@ -9,102 +9,105 @@ use Symfony\Component\Console\Output\OutputInterface; use PP\Lib\Command\AbstractCommand; -class FillUuidCommand extends AbstractCommand { - - /** - * {@inheritdoc} - */ - protected function configure() { - $this - ->setName('db:fill:uuid') - ->setDescription('Fill all empty sys_uuid fields') - ->setHelp('Process concrete datatype, search for empty sys_uuid field and fill it') - ->addArgument('datatype', InputArgument::OPTIONAL, 'Datatype name: struct, html, etc..'); - } - - /** - * {@inheritdoc} - */ - public function execute(InputInterface $input, OutputInterface $output) { - $datatype = $input->getArgument('datatype'); - if ($datatype !== null) { - if (!isset($this->app->types[$datatype])) { - $output->writeln('Error: Unknown datatype '.$datatype); - return 1; - } - - $datatype = $this->app->types[$datatype]; - $this->processDatatype($output, $datatype); - - } else { - foreach ($this->app->types as $datatype) { - $this->processDatatype($output, $datatype); - } - } - - return 0; - } - - /** - * Process datatype and display progress - * - * @param OutputInterface $output - * @param \PXTypeDescription $datatype - */ - protected function processDatatype($output, $datatype) { - - $output->writeln("Processing: ".$datatype->id); - $where = sprintf("(%s is NULL OR %s = '')", OBJ_FIELD_UUID, OBJ_FIELD_UUID); - $count = $this->db->getObjectsByWhere($datatype, null, $where, DB_SELECT_COUNT); - if ($count == 0) { - return; - } - - $batch = 100; - $lastId = 0; - - $sqlSelectFmt = "SELECT id FROM %s WHERE %s AND (%s > %d) ORDER BY %s ASC LIMIT %d"; - $sqlUpdateFmt = "UPDATE %s SET %s = '%s' WHERE %s = %d"; - - // process in batches.. - $progress = new ProgressBar($output, $count); - - while (true) { - $selectSql = sprintf( - $sqlSelectFmt, - $datatype->id, - $where, - OBJ_FIELD_ID, - $lastId, - OBJ_FIELD_ID, - $batch - ); - - $itemList = $this->db->query($selectSql, true); - if (empty($itemList)) { - break; - } - - foreach ($itemList as $item) { - $progress->advance(); - $lastId = $item[OBJ_FIELD_ID]; - - // generate new uuid and update - $uuid = $this->db->EscapeString(Uuid::uuid4()); - $updateSql = sprintf( - $sqlUpdateFmt, - $datatype->id, - OBJ_FIELD_UUID, - $uuid, - OBJ_FIELD_ID, - $item[OBJ_FIELD_ID] - ); - - $this->db->query($updateSql, true); - } - } - - $progress->finish(); - $output->writeln(""); - } +class FillUuidCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('db:fill:uuid') + ->setDescription('Fill all empty sys_uuid fields') + ->setHelp('Process concrete datatype, search for empty sys_uuid field and fill it') + ->addArgument('datatype', InputArgument::OPTIONAL, 'Datatype name: struct, html, etc..'); + } + + /** + * {@inheritdoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $datatype = $input->getArgument('datatype'); + if ($datatype !== null) { + if (!isset($this->app->types[$datatype])) { + $output->writeln('Error: Unknown datatype '.$datatype); + return 1; + } + + $datatype = $this->app->types[$datatype]; + $this->processDatatype($output, $datatype); + + } else { + foreach ($this->app->types as $datatype) { + $this->processDatatype($output, $datatype); + } + } + + return 0; + } + + /** + * Process datatype and display progress + * + * @param OutputInterface $output + * @param \PXTypeDescription $datatype + */ + protected function processDatatype($output, $datatype) + { + + $output->writeln("Processing: ".$datatype->id); + $where = sprintf("(%s is NULL OR %s = '')", OBJ_FIELD_UUID, OBJ_FIELD_UUID); + $count = $this->db->getObjectsByWhere($datatype, null, $where, DB_SELECT_COUNT); + if ($count == 0) { + return; + } + + $batch = 100; + $lastId = 0; + + $sqlSelectFmt = "SELECT id FROM %s WHERE %s AND (%s > %d) ORDER BY %s ASC LIMIT %d"; + $sqlUpdateFmt = "UPDATE %s SET %s = '%s' WHERE %s = %d"; + + // process in batches.. + $progress = new ProgressBar($output, $count); + + while (true) { + $selectSql = sprintf( + $sqlSelectFmt, + $datatype->id, + $where, + OBJ_FIELD_ID, + $lastId, + OBJ_FIELD_ID, + $batch + ); + + $itemList = $this->db->query($selectSql, true); + if (empty($itemList)) { + break; + } + + foreach ($itemList as $item) { + $progress->advance(); + $lastId = $item[OBJ_FIELD_ID]; + + // generate new uuid and update + $uuid = $this->db->EscapeString(Uuid::uuid4()); + $updateSql = sprintf( + $sqlUpdateFmt, + $datatype->id, + OBJ_FIELD_UUID, + $uuid, + OBJ_FIELD_ID, + $item[OBJ_FIELD_ID] + ); + + $this->db->query($updateSql, true); + } + } + + $progress->finish(); + $output->writeln(""); + } } diff --git a/src/Command/GetPropertyCommand.php b/src/Command/GetPropertyCommand.php index bd32bc44..c5107fb6 100644 --- a/src/Command/GetPropertyCommand.php +++ b/src/Command/GetPropertyCommand.php @@ -21,50 +21,52 @@ * Class SetProperty * @package PP\Command */ -class GetPropertyCommand extends AbstractCommand { +class GetPropertyCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('db:property:get') + ->setDescription('Get application property') + ->setHelp('Get property') + ->addArgument('key', InputArgument::REQUIRED, 'property name'); + } - /** - * {@inheritdoc} - */ - protected function configure() { - $this - ->setName('db:property:get') - ->setDescription('Get application property') - ->setHelp('Get property') - ->addArgument('key', InputArgument::REQUIRED, 'property name'); - } + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $key = $input->getArgument('key'); + $stderr = null; - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - $key = $input->getArgument('key'); - $stderr = null; + // in case no tty present.. + if ($output instanceof ConsoleOutputInterface) { + $stderr = $output->getErrorOutput(); + } - // in case no tty present.. - if ($output instanceof ConsoleOutputInterface) { - $stderr = $output->getErrorOutput(); - } + if (empty($key)) { + if ($stderr instanceof OutputInterface) { + $stderr->writeln("Empty key passed"); + return 2; + } + } - if (empty($key)) { - if ($stderr instanceof OutputInterface) { - $stderr->writeln("Empty key passed"); - return 2; - } - } + $sql = sprintf('SELECT "value" FROM %s WHERE "name"=\'%s\'', DT_PROPERTIES, $this->db->EscapeString($key)); + $result = $this->db->query($sql); - $sql = sprintf('SELECT "value" FROM %s WHERE "name"=\'%s\'', DT_PROPERTIES, $this->db->EscapeString($key)); - $result = $this->db->query($sql); + if ((is_countable($result) ? count($result) : 0) === 0) { + if ($stderr instanceof OutputInterface) { + $stderr->writeln("Property: ${key} not found"); + } + return 1; + } - if (count($result) === 0) { - if ($stderr instanceof OutputInterface) { - $stderr->writeln("Property: ${key} not found"); - } - return 1; - } - - $result = array_shift($result); - $output->write($result['value']); - return 0; - } + $result = array_shift($result); + $output->write($result['value']); + return 0; + } } diff --git a/src/Command/Migrate/MigrateCreateCommand.php b/src/Command/Migrate/MigrateCreateCommand.php index 56088bbd..a4116d49 100644 --- a/src/Command/Migrate/MigrateCreateCommand.php +++ b/src/Command/Migrate/MigrateCreateCommand.php @@ -10,43 +10,45 @@ * Class MigrateCreateCommand * @package PP\Command */ -class MigrateCreateCommand extends MigrateAbstractCommand { - - /** - * {@inheritdoc} - */ - protected function configure() { - $this - ->setName('db:migrate:create') - ->setDescription('Create new migration'); - } - - /** - * {@inheritdoc} - */ - public function execute(InputInterface $input, OutputInterface $output) { - $fileName = sprintf("%f", microtime(true)); - $fileName = str_replace(',', '', $fileName); - $fileName = sprintf("%s.php", $fileName); - - $className = $this->getMigrationClass($fileName); - $migrationContent = str_replace( - ['{{namespace}}', '{{class}}'], - [$this->namespace, $className], - $this->template - ); - - $directory = $this->getMigrationsDirectory(); - $writePath = join(DIRECTORY_SEPARATOR, [ - rtrim($directory, DIRECTORY_SEPARATOR), - $fileName - ]); - - if (!is_writable($directory)) { - throw new \Exception("Can't write to directory: ${directory}"); - } - - file_put_contents($writePath, $migrationContent); - $output->writeln("Migration created: ${fileName}"); - } +class MigrateCreateCommand extends MigrateAbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('db:migrate:create') + ->setDescription('Create new migration'); + } + + /** + * {@inheritdoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $fileName = sprintf("%f", microtime(true)); + $fileName = str_replace(',', '', $fileName); + $fileName = sprintf("%s.php", $fileName); + + $className = $this->getMigrationClass($fileName); + $migrationContent = str_replace( + ['{{namespace}}', '{{class}}'], + [$this->namespace, $className], + (string) $this->template + ); + + $directory = $this->getMigrationsDirectory(); + $writePath = join(DIRECTORY_SEPARATOR, [ + rtrim($directory, DIRECTORY_SEPARATOR), + $fileName + ]); + + if (!is_writable($directory)) { + throw new \Exception("Can't write to directory: ${directory}"); + } + + file_put_contents($writePath, $migrationContent); + $output->writeln("Migration created: ${fileName}"); + } } diff --git a/src/Command/Migrate/MigrateListCommand.php b/src/Command/Migrate/MigrateListCommand.php index 1a094f9c..15f8bf73 100644 --- a/src/Command/Migrate/MigrateListCommand.php +++ b/src/Command/Migrate/MigrateListCommand.php @@ -10,30 +10,32 @@ * Class MigrateListCommand * @package PP\Command */ -class MigrateListCommand extends MigrateAbstractCommand { +class MigrateListCommand extends MigrateAbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('db:migrate:list') + ->setDescription('Display pending migrations'); + } - /** - * {@inheritdoc} - */ - protected function configure() { - $this - ->setName('db:migrate:list') - ->setDescription('Display pending migrations'); - } + /** + * {@inheritdoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $migrationList = $this->getPendingMigrations(); + if (count($migrationList) === 0) { + $output->writeln("No pending migrations"); + return 0; + } - /** - * {@inheritdoc} - */ - public function execute(InputInterface $input, OutputInterface $output) { - $migrationList = $this->getPendingMigrations(); - if (count($migrationList) === 0) { - $output->writeln("No pending migrations"); - return 0; - } - - $output->writeln("Pending migrations list:"); - foreach ($migrationList as $migration) { - $output->writeln("${migration}"); - } - } + $output->writeln("Pending migrations list:"); + foreach ($migrationList as $migration) { + $output->writeln("${migration}"); + } + } } diff --git a/src/Command/Migrate/MigrateUpCommand.php b/src/Command/Migrate/MigrateUpCommand.php index efa120ed..89365568 100644 --- a/src/Command/Migrate/MigrateUpCommand.php +++ b/src/Command/Migrate/MigrateUpCommand.php @@ -11,82 +11,85 @@ * Class MigrateCommand * @package PP\Command */ -class MigrateUpCommand extends MigrateAbstractCommand { - - /** - * {@inheritdoc} - */ - protected function configure() { - $this - ->setName('db:migrate:up') - ->setDescription('Migrate all pending migrations'); - } - - /** - * {@inheritdoc} - */ - public function execute(InputInterface $input, OutputInterface $output) { - $migrationList = $this->getPendingMigrations(); - if (count($migrationList) === 0) { - $output->writeln("No pending migrations"); - return 0; - } - - $this->dbDriver->transactionBegin(); - $output->writeln("Starting migrations"); - try { - $migrationPath = $this->getMigrationsDirectory(); - - foreach ($migrationList as $fileName) { - $className = $this->getMigrationClassWithNamespace($fileName); - $requirePath = join(DIRECTORY_SEPARATOR, [$migrationPath, $fileName]); - $classInstance = $this->getClassInstance($requirePath, $className); - - // filling up migration list of sql code - $classInstance->up(); - - // formatting set of instructions - $sqlList = $classInstance->getSqlList(); - $sqlList[] = $this->getMigrationSqlFinalizer($fileName); - - // applying sql - foreach ($sqlList as $sql) { - $result = $this->dbDriver->ModifyingQuery($sql); - if ($result === ERROR_DB_BADQUERY || $result === ERROR_DB_CANNOTCONNECT) { - throw new \Exception("Migration failed: ${fileName}"); - } - } - - $output->writeln("${fileName} migrated successfully"); - } - - $this->dbDriver->transactionCommit(); - $output->writeln("Done!"); - - } catch (\Exception $e) { - $this->dbDriver->transactionRollback(); - throw $e; - } - } - - /** - * @param string $filePath - * @param string $className - * @return MigrationAbstract - * @throws \Exception - */ - protected function getClassInstance($filePath, $className) { - require_once $filePath; - - if (!class_exists($className)) { - throw new \Exception("Class: ${className} doesn't exist"); - } - - $classInstance = new $className(); - if (!$classInstance instanceof MigrationAbstract) { - throw new \Exception("Class: ${className} is not instance of AbstractMigration"); - } - - return $classInstance; - } +class MigrateUpCommand extends MigrateAbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('db:migrate:up') + ->setDescription('Migrate all pending migrations'); + } + + /** + * {@inheritdoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $migrationList = $this->getPendingMigrations(); + if (count($migrationList) === 0) { + $output->writeln("No pending migrations"); + return 0; + } + + $this->dbDriver->transactionBegin(); + $output->writeln("Starting migrations"); + try { + $migrationPath = $this->getMigrationsDirectory(); + + foreach ($migrationList as $fileName) { + $className = $this->getMigrationClassWithNamespace($fileName); + $requirePath = join(DIRECTORY_SEPARATOR, [$migrationPath, $fileName]); + $classInstance = $this->getClassInstance($requirePath, $className); + + // filling up migration list of sql code + $classInstance->up(); + + // formatting set of instructions + $sqlList = $classInstance->getSqlList(); + $sqlList[] = $this->getMigrationSqlFinalizer($fileName); + + // applying sql + foreach ($sqlList as $sql) { + $result = $this->dbDriver->ModifyingQuery($sql); + if ($result === ERROR_DB_BADQUERY || $result === ERROR_DB_CANNOTCONNECT) { + throw new \Exception("Migration failed: ${fileName}"); + } + } + + $output->writeln("${fileName} migrated successfully"); + } + + $this->dbDriver->transactionCommit(); + $output->writeln("Done!"); + + } catch (\Exception $e) { + $this->dbDriver->transactionRollback(); + throw $e; + } + } + + /** + * @param string $filePath + * @param string $className + * @return MigrationAbstract + * @throws \Exception + */ + protected function getClassInstance($filePath, $className) + { + require_once $filePath; + + if (!class_exists($className)) { + throw new \Exception("Class: ${className} doesn't exist"); + } + + $classInstance = new $className(); + if (!$classInstance instanceof MigrationAbstract) { + throw new \Exception("Class: ${className} is not instance of AbstractMigration"); + } + + return $classInstance; + } } diff --git a/src/Command/SetPropertyCommand.php b/src/Command/SetPropertyCommand.php index dc18af65..c87b3d59 100644 --- a/src/Command/SetPropertyCommand.php +++ b/src/Command/SetPropertyCommand.php @@ -14,51 +14,53 @@ * Class SetProperty * @package PP\Command */ -class SetPropertyCommand extends AbstractCommand { +class SetPropertyCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('db:property:set') + ->setDescription('Set application property') + ->setHelp('Set property') + ->addArgument('key', InputArgument::REQUIRED, 'property name') + ->addArgument('val', InputArgument::OPTIONAL, 'property value', '') + ->addArgument('description', InputArgument::OPTIONAL, 'property description', ''); + } - /** - * {@inheritdoc} - */ - protected function configure() { - $this - ->setName('db:property:set') - ->setDescription('Set application property') - ->setHelp('Set property') - ->addArgument('key', InputArgument::REQUIRED, 'property name') - ->addArgument('val', InputArgument::OPTIONAL, 'property value', '') - ->addArgument('description', InputArgument::OPTIONAL, 'property description', ''); - } + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $key = $input->getArgument('key'); + $val = $input->getArgument('val'); + $description = $input->getArgument('description'); - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) { - $key = $input->getArgument('key'); - $val = $input->getArgument('val'); - $description = $input->getArgument('description'); + $dbFields = ['name', 'value']; + $dbValues = [$key, $val]; - $dbFields = ['name', 'value']; - $dbValues = [$key, $val]; + if (!empty($description)) { + $dbFields[] = 'description'; + $dbValues[] = $description; + } - if (!empty($description)) { - $dbFields[] = 'description'; - $dbValues[] = $description; - } + $sql = sprintf('SELECT id FROM %s WHERE "name"=\'%s\'', DT_PROPERTIES, $this->db->EscapeString($key)); + $result = $this->db->query($sql); + if ((is_countable($result) ? count($result) : 0) > 0) { + $result = array_flat($result[0], 'id'); - $sql = sprintf('SELECT id FROM %s WHERE "name"=\'%s\'', DT_PROPERTIES, $this->db->EscapeString($key)); - $result = $this->db->query($sql); - if (count($result) > 0) { - $result = array_flat($result[0], 'id'); + $this->db->UpdateObjectById(DT_PROPERTIES, $result['id'], $dbFields, $dbValues); + $output->writeln("Property: ${key}: updated"); - $this->db->UpdateObjectById(DT_PROPERTIES, $result['id'], $dbFields, $dbValues); - $output->writeln("Property: ${key}: updated"); + } else { + $dbFields[] = 'sys_uuid'; + $dbValues[] = Uuid::uuid4()->toString(); - } else { - $dbFields[] = 'sys_uuid'; - $dbValues[] = Uuid::uuid4()->toString(); - - $this->db->InsertObject(DT_PROPERTIES, $dbFields, $dbValues); - $output->writeln("Property: ${key}: inserted"); - } - } + $this->db->InsertObject(DT_PROPERTIES, $dbFields, $dbValues); + $output->writeln("Property: ${key}: inserted"); + } + } } diff --git a/src/ConfigurationLocator.php b/src/ConfigurationLocator.php index c67e868e..43e63236 100644 --- a/src/ConfigurationLocator.php +++ b/src/ConfigurationLocator.php @@ -10,50 +10,49 @@ * * @package PP */ -class ConfigurationLocator { - - /** - * @var FileLocatorInterface - */ - protected $locator; - - /** - * ConfigurationLocator constructor. - * - * @param FileLocatorInterface $locator - */ - public function __construct(FileLocatorInterface $locator) { - $this->locator = $locator; - } - - /** - * Locates the file. - * - * @param string $name - * @param bool $first - * @return array|string - */ - public function locate($name, $first = true) { - return $this->locator->locate($name, null, $first); - } - - /** - * Same as locate but suppress all exceptions if file's absent. - * - * @param string $name - * @param bool $first - * @return array|string - */ - public function locateQuiet($name, $first = true) { - $paths = $first ? '' : []; - - try { - $paths = $this->locate($name, $first); - } catch (FileLocatorFileNotFoundException $ex) { - // - } - - return $paths; - } +class ConfigurationLocator +{ + /** + * @var FileLocatorInterface + */ + protected $locator; + + /** + * ConfigurationLocator constructor. + */ + public function __construct(FileLocatorInterface $locator) + { + $this->locator = $locator; + } + + /** + * Locates the file. + * + * @param string $name + * @param bool $first + */ + public function locate($name, $first = true): array|string + { + return $this->locator->locate($name, null, $first); + } + + /** + * Same as locate but suppress all exceptions if file's absent. + * + * @param string $name + * @param bool $first + */ + public function locateQuiet($name, $first = true): array|string + { + $paths = $first ? '' : []; + + try { + $paths = $this->locate($name, $first); + } catch (FileLocatorFileNotFoundException) { + // + } + + return $paths; + } } diff --git a/src/ConsoleApplication.php b/src/ConsoleApplication.php index 47aa3c77..ed39ee53 100644 --- a/src/ConsoleApplication.php +++ b/src/ConsoleApplication.php @@ -7,6 +7,7 @@ use PP\Properties\EnvLoader; use Symfony\Component\Console\Application; use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\InputInterface; @@ -36,208 +37,213 @@ * * @package PP */ -class ConsoleApplication extends Application { - - use ContainerAwareTrait; - - /** Proxima application instance */ - protected $app; - - /** - * @inheritdoc - */ - protected function getDefaultInputDefinition() { - $definition = parent::getDefaultInputDefinition(); - - $definition->addOptions([ - new InputOption('mail', 'm', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, - 'Email addresses for report. For example, --mail=mail@domain.com --mail=test@test.ru'), - new InputOption('send-report', 'S', InputOption::VALUE_NONE, 'Send report after command execution') - ]); - - return $definition; - } - - public static function start() { - $app = new static('pp', PP_VERSION); - - // set command loader - $dispatcher = new EventDispatcher(); - $dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) use ($app) { - $app->consoleCommandHandler($event); - }); - - $dispatcher->addListener(ConsoleEvents::TERMINATE, function (ConsoleTerminateEvent $event) use ($app) { - $app->consoleTerminateHandler($event); - }); - - // create and run command set.. - $app->setDispatcher($dispatcher); - $app->registerCoreCommands(); - $app->registerProjectCommands(); - $app->run(new ArgvInput(), new BuferringOutput()); - } - - /** - * Handles console command event of application. - * - * @param ConsoleCommandEvent $event - */ - protected function consoleCommandHandler(ConsoleCommandEvent $event) { - $cmd = $event->getCommand(); - - if ($cmd instanceof AbstractCommand) { - $this->commonCommandHandler($event); - } - - if ($cmd instanceof MigrateAbstractCommand) { - $this->migrateCommandHandler($event); - } - } - - /** - * Configures common command before executing. - * - * @param ConsoleCommandEvent $event - */ - protected function commonCommandHandler(ConsoleCommandEvent $event) { - $cmd = $event->getCommand(); - - $engine = (new \PXEngineSbin())->start(); - $this->app = PXRegistry::getApp(); - $cmd->setContainer($engine->getContainer()); - $cmd->setApp($this->app) - ->setDb(PXRegistry::getDb()); - - $address = $this->getTo($event->getInput()); - if ($this->isReporting($event) && !empty($address)) { - $text = [ - 'After command execution auto-report will be sent to next e-mails:', - $address, - '' - ]; - } else { - $text = [ - 'Auto-report will not be sent after command execution', - empty($address) ? 'No e-mail addresses' : '', - '' - ]; - } - $event->getOutput()->writeln($text, OutputInterface::VERBOSITY_QUIET); - } - - /** - * Configures migration-type command before execution. - * - * @param ConsoleCommandEvent $event - */ - protected function migrateCommandHandler(ConsoleCommandEvent $event) { - $cmd = $event->getCommand(); - - EnvLoader::inject(); - $dbDescription = \NLDBDescription::fromEnv(); - $cmd->setDbDriver($dbDescription->getDriver()); - } - - /** - * Handles console terminate event of application. - * Collects command output and sends email report. - * - * @param ConsoleTerminateEvent $event - * @throws \Exception - */ - protected function consoleTerminateHandler(ConsoleTerminateEvent $event) { - if (!$this->isReporting($event)) { - return; - } - - $cmd = $event->getCommand(); - $input = $event->getInput(); - $output = $event->getOutput(); - - $project = $this->app->getProperty('SYS_PROJECT_NAME', ''); - $from = $this->app->getProperty('SYS_COMMAND_REPORT_FROM', ''); - - $reporter = new Mailer(); - $reporter->setCommandName($cmd->getName()) - ->setOptions($input->getOptions()) - ->setProjectName($project) - ->setFrom($from) - ->setTo($this->getTo($input)) - ->sendReport($output->fetch()); - } - - /** - * Checks if reporting is available and enabled. - * - * @param ConsoleEvent $event - * @return bool - */ - protected function isReporting(ConsoleEvent $event) { - $cmd = $event->getCommand(); - $input = $event->getInput(); - - return $cmd instanceof AbstractCommand - && $input->getOption('send-report'); - } - - /** - * Gets mail list from options, env variable and property. - * - * @param InputInterface $input - * @return string - */ - protected function getTo(InputInterface $input) { - return join(',', $input->getOption('mail')) - ?: EnvLoader::get('PP_COMMAND_REPORT_MAIL') - ?: $this->app->getProperty('SYS_COMMAND_REPORT_MAIL', ''); - } - - /** - * Register bundled commands. Those commands should be available - * to every project. - * - * @return $this - */ - protected function registerCoreCommands() { - $this->add(new CronCommand()); - $this->add(new GetPropertyCommand()); - $this->add(new SetPropertyCommand()); - $this->add(new FillMetaCommand()); - $this->add(new FillUuidCommand()); - $this->add(new MigrateListCommand()); - $this->add(new MigrateUpCommand()); - $this->add(new MigrateCreateCommand()); - - return $this; - } - - /** - * Load project commands (if exists). - * - * @return $this - * @throws \Exception - */ - protected function registerProjectCommands() { - $filePath = BASEPATH . '/app/config/commands.yml'; - $filePath = path_clear($filePath); - - if (file_exists($filePath)) { - $result = Yaml::parse(file_get_contents($filePath)); - $commandList = isset($result['commands']) && is_array($result['commands']) - ? $result['commands'] - : []; - - foreach ($commandList as $className) { - if (!class_exists($className)) { - throw new \Exception("Command class '${className}' doesn't exist"); - } - - $this->add(new $className()); - } - } - - return $this; - } +class ConsoleApplication extends Application +{ + use ContainerAwareTrait; + + /** Proxima application instance */ + protected $app; + + /** + * @inheritdoc + */ + protected function getDefaultInputDefinition(): InputDefinition + { + $definition = parent::getDefaultInputDefinition(); + + $definition->addOptions([ + new InputOption( + 'mail', + 'm', + InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, + 'Email addresses for report. For example, --mail=mail@domain.com --mail=test@test.ru' + ), + new InputOption('send-report', 'S', InputOption::VALUE_NONE, 'Send report after command execution') + ]); + + return $definition; + } + + public static function start() + { + $app = new static('pp', PP_VERSION); + + // set command loader + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) use ($app) { + $app->consoleCommandHandler($event); + }); + + $dispatcher->addListener(ConsoleEvents::TERMINATE, function (ConsoleTerminateEvent $event) use ($app) { + $app->consoleTerminateHandler($event); + }); + + // create and run command set.. + $app->setDispatcher($dispatcher); + $app->registerCoreCommands(); + $app->registerProjectCommands(); + $app->run(new ArgvInput(), new BuferringOutput()); + } + + /** + * Handles console command event of application. + */ + protected function consoleCommandHandler(ConsoleCommandEvent $event) + { + $cmd = $event->getCommand(); + + if ($cmd instanceof AbstractCommand) { + $this->commonCommandHandler($event); + } + + if ($cmd instanceof MigrateAbstractCommand) { + $this->migrateCommandHandler($event); + } + } + + /** + * Configures common command before executing. + */ + protected function commonCommandHandler(ConsoleCommandEvent $event) + { + $cmd = $event->getCommand(); + + $engine = (new \PXEngineSbin())->start(); + $this->app = PXRegistry::getApp(); + $cmd->setContainer($engine->getContainer()); + $cmd->setApp($this->app) + ->setDb(PXRegistry::getDb()); + + $address = $this->getTo($event->getInput()); + if ($this->isReporting($event) && !empty($address)) { + $text = [ + 'After command execution auto-report will be sent to next e-mails:', + $address, + '' + ]; + } else { + $text = [ + 'Auto-report will not be sent after command execution', + empty($address) ? 'No e-mail addresses' : '', + '' + ]; + } + $event->getOutput()->writeln($text, OutputInterface::VERBOSITY_QUIET); + } + + /** + * Configures migration-type command before execution. + */ + protected function migrateCommandHandler(ConsoleCommandEvent $event) + { + $cmd = $event->getCommand(); + + EnvLoader::inject(); + $dbDescription = \NLDBDescription::fromEnv(); + $cmd->setDbDriver($dbDescription->getDriver()); + } + + /** + * Handles console terminate event of application. + * Collects command output and sends email report. + * + * @throws \Exception + */ + protected function consoleTerminateHandler(ConsoleTerminateEvent $event) + { + if (!$this->isReporting($event)) { + return; + } + + $cmd = $event->getCommand(); + $input = $event->getInput(); + $output = $event->getOutput(); + + $project = $this->app->getProperty('SYS_PROJECT_NAME', ''); + $from = $this->app->getProperty('SYS_COMMAND_REPORT_FROM', ''); + + $reporter = new Mailer(); + $reporter->setCommandName($cmd->getName()) + ->setOptions($input->getOptions()) + ->setProjectName($project) + ->setFrom($from) + ->setTo($this->getTo($input)) + ->sendReport($output->fetch()); + } + + /** + * Checks if reporting is available and enabled. + * + * @return bool + */ + protected function isReporting(ConsoleEvent $event) + { + $cmd = $event->getCommand(); + $input = $event->getInput(); + + return $cmd instanceof AbstractCommand + && $input->getOption('send-report'); + } + + /** + * Gets mail list from options, env variable and property. + * + * @return string + */ + protected function getTo(InputInterface $input) + { + return join(',', $input->getOption('mail')) + ?: EnvLoader::get('PP_COMMAND_REPORT_MAIL') + ?: $this->app->getProperty('SYS_COMMAND_REPORT_MAIL', ''); + } + + /** + * Register bundled commands. Those commands should be available + * to every project. + * + * @return $this + */ + protected function registerCoreCommands() + { + $this->add(new CronCommand()); + $this->add(new GetPropertyCommand()); + $this->add(new SetPropertyCommand()); + $this->add(new FillMetaCommand()); + $this->add(new FillUuidCommand()); + $this->add(new MigrateListCommand()); + $this->add(new MigrateUpCommand()); + $this->add(new MigrateCreateCommand()); + + return $this; + } + + /** + * Load project commands (if exists). + * + * @return $this + * @throws \Exception + */ + protected function registerProjectCommands() + { + $filePath = BASEPATH . '/app/config/commands.yml'; + $filePath = path_clear($filePath); + + if (file_exists($filePath)) { + $result = Yaml::parse(file_get_contents($filePath)); + $commandList = isset($result['commands']) && is_array($result['commands']) + ? $result['commands'] + : []; + + foreach ($commandList as $className) { + if (!class_exists($className)) { + throw new \Exception("Command class '${className}' doesn't exist"); + } + + $this->add(new $className()); + } + } + + return $this; + } } diff --git a/src/Cron/AbstractCron.php b/src/Cron/AbstractCron.php index a57ab0af..fcb2e20a 100644 --- a/src/Cron/AbstractCron.php +++ b/src/Cron/AbstractCron.php @@ -12,44 +12,46 @@ * * @package PP\Cron */ -abstract class AbstractCron implements ContainerAwareInterface { - - use ContainerAwareTrait; - - /** - * TODO: should be protected - * @var string - */ - public $name = 'Abstract CronRun Class'; - - /** - * @param \PXApplication $app - * @param \PXDatabase|PostgreSqlDriver $db - * @param Tree $tree - * @param int $matchedTime - * @param CronRule $matchedRule - * - * @return array - */ - public function Run($app, $db, $tree, $matchedTime, $matchedRule) { - return [ - 'status' => -1, - 'note' => 'Не определен метод Run()' - ]; - } - - /** - * @return string - */ - public function getName() { - return $this->name; - } - - protected function log($message) { - \PXRegistry::getLogger(LOGGER_CRON)->info($message); - } - - protected function error($message) { - \PXRegistry::getLogger(LOGGER_CRON)->error($message); - } +abstract class AbstractCron implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + /** + * TODO: should be protected + * @var string + */ + public $name = 'Abstract CronRun Class'; + + /** + * @param \PXApplication $app + * @param Tree $tree + * @param int $matchedTime + * @param CronRule $matchedRule + * @return array + */ + public function Run($app, \PXDatabase|\PP\Lib\Database\Driver\PostgreSqlDriver $db, $tree, $matchedTime, $matchedRule) + { + return [ + 'status' => -1, + 'note' => 'Не определен метод Run()' + ]; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + protected function log($message) + { + \PXRegistry::getLogger(LOGGER_CRON)->info($message); + } + + protected function error($message) + { + \PXRegistry::getLogger(LOGGER_CRON)->error($message); + } } diff --git a/src/Cron/CronRule.php b/src/Cron/CronRule.php index 2169346b..d06ff496 100644 --- a/src/Cron/CronRule.php +++ b/src/Cron/CronRule.php @@ -8,122 +8,121 @@ */ class CronRule { - - public $valid; - public $asString; - public $match; - public $matchHash; - - public function __construct($ruleString) - { - $this->valid = false; - $this->asString = $ruleString; - - $params = preg_split('/\s+/' . REGEX_MOD, $ruleString); - - if (count($params) != 5) { - return; - } - - $this->match['min'] = $this->_parse($params[0], 0, 59); - $this->match['hour'] = $this->_parse($params[1], 0, 23); - $this->match['mday'] = $this->_parse($params[2], 1, 31); - $this->match['mon'] = $this->_parse($params[3], 1, 12); - $this->match['wday'] = $this->_parse($params[4], 0, 7); - - if (in_array(null, $this->match)) { - return; - } - - if (isset($this->match['wday'][7])) { - unset($this->match['wday'][7]); - $this->match['wday'][0] = true; - } - - $this->matchHash = md5(serialize($this->match)); - - $this->valid = true; - } - - public function _parse($s, $min, $max) - { - $result = []; - $s = strtolower($s); - - $s = strtr($s, - [ - 'sun' => '0', - 'mon' => '1', - 'tue' => '2', - 'wed' => '3', - 'thu' => '4', - 'fri' => '5', - 'sat' => '6', - ] - ); - - $s = strtr($s, - [ - 'jan' => '1', - 'feb' => '2', - 'mar' => '3', - 'apr' => '4', - 'may' => '5', - 'jun' => '6', - 'jul' => '7', - 'aug' => '8', - 'sep' => '9', - 'oct' => '10', - 'nov' => '11', - 'dec' => '12', - ] - ); - - $params = explode(',', $s); - foreach ($params as $k) { - $step = 1; - - if (preg_match('#^(.+?)/(\d+)$#' . REGEX_MOD, $k, $m)) { - $k = $m[1]; - $step = (int)$m[2]; - - if ($step <= 0 || $step >= $max) { - return null; - } - } - - if (preg_match('#^(\d+)-(\d+)$#' . REGEX_MOD, $k, $m)) { - if ($m[1] >= $m[2]) { - return null; - } - - if ($m[1] < $min || $m[2] > $max) { - return null; - } - - for ($i = $m[1]; $i <= $m[2]; $i += $step) { - $result[$i] = true; - } - - } else if (preg_match('#^(\d+|\*)$#' . REGEX_MOD, $k, $m)) { - if ($m[1] == '*') { - for ($i = $min; $i <= $max; $i += $step) { - $result[$i] = true; - } - - } else { - if ($m[1] < $min || $m[1] > $max) { - return null; - } - - $result[$m[1]] = true; - } - - } else { - return null; - } - } - - return $result; - } + public $valid; + public $match; + public $matchHash; + + public function __construct(public $asString) + { + $this->valid = false; + + $params = preg_split('/\s+/' . REGEX_MOD, (string) $asString); + + if ((is_countable($params) ? count($params) : 0) != 5) { + return; + } + + $this->match['min'] = $this->_parse($params[0], 0, 59); + $this->match['hour'] = $this->_parse($params[1], 0, 23); + $this->match['mday'] = $this->_parse($params[2], 1, 31); + $this->match['mon'] = $this->_parse($params[3], 1, 12); + $this->match['wday'] = $this->_parse($params[4], 0, 7); + + if (in_array(null, $this->match)) { + return; + } + + if (isset($this->match['wday'][7])) { + unset($this->match['wday'][7]); + $this->match['wday'][0] = true; + } + + $this->matchHash = md5(serialize($this->match)); + + $this->valid = true; + } + + public function _parse($s, $min, $max) + { + $result = []; + $s = strtolower((string) $s); + + $s = strtr( + $s, + [ + 'sun' => '0', + 'mon' => '1', + 'tue' => '2', + 'wed' => '3', + 'thu' => '4', + 'fri' => '5', + 'sat' => '6', + ] + ); + + $s = strtr( + $s, + [ + 'jan' => '1', + 'feb' => '2', + 'mar' => '3', + 'apr' => '4', + 'may' => '5', + 'jun' => '6', + 'jul' => '7', + 'aug' => '8', + 'sep' => '9', + 'oct' => '10', + 'nov' => '11', + 'dec' => '12', + ] + ); + + $params = explode(',', $s); + foreach ($params as $k) { + $step = 1; + + if (preg_match('#^(.+?)/(\d+)$#' . REGEX_MOD, $k, $m)) { + $k = $m[1]; + $step = (int)$m[2]; + + if ($step <= 0 || $step >= $max) { + return null; + } + } + + if (preg_match('#^(\d+)-(\d+)$#' . REGEX_MOD, $k, $m)) { + if ($m[1] >= $m[2]) { + return null; + } + + if ($m[1] < $min || $m[2] > $max) { + return null; + } + + for ($i = $m[1]; $i <= $m[2]; $i += $step) { + $result[$i] = true; + } + + } elseif (preg_match('#^(\d+|\*)$#' . REGEX_MOD, $k, $m)) { + if ($m[1] == '*') { + for ($i = $min; $i <= $max; $i += $step) { + $result[$i] = true; + } + + } else { + if ($m[1] < $min || $m[1] > $max) { + return null; + } + + $result[$m[1]] = true; + } + + } else { + return null; + } + } + + return $result; + } } diff --git a/src/DependencyInjection/Compiler/AddLoggingHandlersPass.php b/src/DependencyInjection/Compiler/AddLoggingHandlersPass.php index 23d37aaa..4484ce96 100644 --- a/src/DependencyInjection/Compiler/AddLoggingHandlersPass.php +++ b/src/DependencyInjection/Compiler/AddLoggingHandlersPass.php @@ -6,22 +6,20 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; -class AddLoggingHandlersPass implements CompilerPassInterface { +class AddLoggingHandlersPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->has('logger')) { + return; + } - /** - * @param ContainerBuilder $container - */ - public function process(ContainerBuilder $container) { - if (!$container->has('logger')) { - return; - } + $logger = $container->getDefinition('logger'); + $serviceIds = $container->findTaggedServiceIds('logger.handler'); - $logger = $container->getDefinition('logger'); - $serviceIds = $container->findTaggedServiceIds('logger.handler'); - - foreach ($serviceIds as $id => $tags) { - $logger->addMethodCall('pushHandler', [new Reference($id)]); - } - } + foreach ($serviceIds as $id => $tags) { + $logger->addMethodCall('pushHandler', [new Reference($id)]); + } + } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 5218d0f5..87b27332 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -10,17 +10,18 @@ * * @package PP\DependencyInjection */ -class Configuration implements ConfigurationInterface { +class Configuration implements ConfigurationInterface +{ + public function getConfigTreeBuilder() + { + $tree = new TreeBuilder('core'); + $rootNode = $tree->getRootNode(); - public function getConfigTreeBuilder() { - $tree = new TreeBuilder(); - $rootNode = $tree->root('core'); + $rootNode->children() + ->scalarNode('application')->defaultValue('app')->end() + ->end(); - $rootNode->children() - ->scalarNode('application')->defaultValue('app')->end() - ->end(); - - return $tree; - } + return $tree; + } } diff --git a/src/DependencyInjection/CoreExtension.php b/src/DependencyInjection/CoreExtension.php index f6f08048..b147e2cb 100644 --- a/src/DependencyInjection/CoreExtension.php +++ b/src/DependencyInjection/CoreExtension.php @@ -13,45 +13,46 @@ * * @package PP\DependencyInjection */ -class CoreExtension extends Extension { - - /** - * @inheritdoc - * @throws \Exception - */ - public function load(array $configs, ContainerBuilder $container) { - $loader = new YamlFileLoader($container, new FileLocator(PPSERVICESPATH)); - - $configuration = $this->getConfiguration($configs, $container); - $config = $this->processConfiguration($configuration, $configs); - - $container->setParameter('core.base_dir', BASEPATH); - $container->setParameter('core.app_dir', APPPATH); - $container->setParameter('core.cache_dir', CACHE_PATH); - $container->setParameter('core.runtime_dir', RUNTIME_PATH); - - // register event dispatcher configuration - $loader->load('event_dispatcher.yml'); - - $this->registerLoggerConfiguration($config['application'], $container, $loader); - } - - /** - * @param string $applicationName - * @param ContainerBuilder $container - * @param LoaderInterface $loader - * @throws \Exception - */ - private function registerLoggerConfiguration($applicationName, ContainerBuilder $container, LoaderInterface $loader) { - $loader->load('logger.yml'); - $container->getDefinition('logger')->addArgument($applicationName); - } - - /** - * @inheritdoc - */ - public function getAlias() { - return 'core'; - } +class CoreExtension extends Extension +{ + /** + * @inheritdoc + * @throws \Exception + */ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new YamlFileLoader($container, new FileLocator(PPSERVICESPATH)); + + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + $container->setParameter('core.base_dir', BASEPATH); + $container->setParameter('core.app_dir', APPPATH); + $container->setParameter('core.cache_dir', CACHE_PATH); + $container->setParameter('core.runtime_dir', RUNTIME_PATH); + + // register event dispatcher configuration + $loader->load('event_dispatcher.yml'); + + $this->registerLoggerConfiguration($config['application'], $container, $loader); + } + + /** + * @param string $applicationName + * @throws \Exception + */ + private function registerLoggerConfiguration($applicationName, ContainerBuilder $container, LoaderInterface $loader) + { + $loader->load('logger.yml'); + $container->getDefinition('logger')->addArgument($applicationName); + } + + /** + * @inheritdoc + */ + public function getAlias(): string + { + return 'core'; + } } diff --git a/src/Lib/ArrayCollection.php b/src/Lib/ArrayCollection.php index 8012d764..91d1e916 100644 --- a/src/Lib/ArrayCollection.php +++ b/src/Lib/ArrayCollection.php @@ -6,84 +6,88 @@ * Class PropertyCollection * @package PP\Lib */ -class ArrayCollection extends Collection implements \ArrayAccess { +class ArrayCollection extends Collection implements \ArrayAccess +{ + /** + * {@inheritdoc} + */ + public function offsetExists($offset) + { + return array_key_exists($offset, $this->elements); + } - /** - * {@inheritdoc} - */ - public function offsetExists($offset) { - return array_key_exists($offset, $this->elements); - } + /** + * {@inheritdoc} + */ + public function offsetGet($offset) + { + return ($this->offsetExists($offset)) + ? $this->elements[$offset] + : null; + } - /** - * {@inheritdoc} - */ - public function offsetGet($offset) { - return ($this->offsetExists($offset)) - ? $this->elements[$offset] - : null; - } + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value) + { + $this->elements[$offset] = $value; + } - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $value) { - $this->elements[$offset] = $value; - } + /** + * {@inheritdoc} + */ + public function offsetUnset($offset) + { + if ($this->offsetExists($offset)) { + unset($this->elements[$offset]); + } + } - /** - * {@inheritdoc} - */ - public function offsetUnset($offset) { - if ($this->offsetExists($offset)) { - unset($this->elements[$offset]); - } - } + /** + * Build collection from key/value array + * + * @param $sourceList + */ + public function fromArray($sourceList) + { + foreach ($sourceList as $key => $value) { + $this->offsetSet($key, $value); + } + } - /** - * Build collection from key/value array - * - * @param $sourceList - */ - public function fromArray($sourceList) { - foreach ($sourceList as $key => $value) { - $this->offsetSet($key, $value); - } - } + /** + * Get value from nested arrays defined by path. + * + * @param string $path + * @param string $delimiter + * @return mixed + */ + public function getByPath($path, $delimiter = '.') + { + return $this->getByPathFromArray($path, $delimiter, $this); + } - /** - * Get value from nested arrays defined by path. - * - * @param string $path - * @param string $delimiter - * @return mixed - */ - public function getByPath($path, $delimiter='.') { - return $this->getByPathFromArray($path, $delimiter, $this); - } + /** + * @param string $path + * @param string $delimiter + * @return mixed + */ + protected function getByPathFromArray($path, $delimiter, array|\PP\Lib\ArrayCollection &$from) + { + $keyList = explode($delimiter, $path); + $key = array_shift($keyList); - /** - * @param string $path - * @param string $delimiter - * @param array|ArrayCollection $from - * @return mixed - */ - protected function getByPathFromArray($path, $delimiter, &$from) { - $keyList = explode($delimiter, $path); - $key = array_shift($keyList); + $result = $from[$key] ?? null; - $result = isset($from[$key]) - ? $from[$key] - : null; + if ($result === null) { + return $result; + } - if ($result === null) { - return $result; - } - - if (!empty($keyList)) { - return $this->getByPathFromArray(implode($delimiter, $keyList), $delimiter, $result); - } else { - return $result; - } - } + if (!empty($keyList)) { + return $this->getByPathFromArray(implode($delimiter, $keyList), $delimiter, $result); + } else { + return $result; + } + } } diff --git a/src/Lib/Auth/AuthAbstract.php b/src/Lib/Auth/AuthAbstract.php index 34e7fa7b..de0ce78b 100644 --- a/src/Lib/Auth/AuthAbstract.php +++ b/src/Lib/Auth/AuthAbstract.php @@ -11,155 +11,154 @@ abstract class AuthAbstract implements AuthInterface { - - /** @var PXRequest */ - protected $request; - - /** @var PXDatabase */ - protected $db; - - /** @var PXApplication */ - protected $app; - - /** @var PXUser */ - protected $user; - - /** @var null|SessionInterface */ - protected $session; - - /** @var @var string|null */ - protected $login; - - /** @var @var string|null */ - protected $passwd; - - public function __construct(?array $params = []) - { - // params is not used right now.. - } - - /** - * {@inheritdoc} - */ - public function setRequest(PXRequest $request): AuthInterface - { - $this->request = $request; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setDb(PXDatabase $db): AuthInterface - { - $this->db = $db; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setApp(PXApplication $app): AuthInterface - { - $this->app = $app; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setUser(PXUser $user): AuthInterface - { - $this->user = $user; - - return $this; - } - - /** - * @param null|SessionInterface $session - * @return $this - */ - public function setSession(?SessionInterface $session = null): AuthInterface - { - $this->session = $session; - - return $this; - } - - /** - * {@inheritdoc} - */ - abstract public function isCredentialsValid(array $credentials): bool; - - /** - * {@inheritdoc} - */ - abstract public function isAuthorized(): bool; - - public function fillUserFields(array $uArray): void - { - $user = $this->user ?: PXRegistry::getUser(); - - $user->id = $uArray['id'] ?? null; - $user->login = $uArray['title'] ?? null; - $user->data = $uArray; - $this->passwd = $user->passwd = $uArray['passwd'] ?? null; - } - - public function getTitle(): ?string - { - return $this->user->login; - } - - protected function findUser(): ?array - { - if (!mb_strlen($this->login)) { - return null; - } - - $tmp = $this->db->getObjectsByFieldLimited( - $this->app->types[DT_USER], - true, - 'title', - $this->login, - 1, - 0 - ); - - return count($tmp) ? current($tmp) : null; - } - - public function auth(): bool - { - return true; - } - - public function unAuth(): bool - { - return true; - } - - /** - * Метод-триггер, вызывается в PXUser::checkAuth() после загрузки правил acl, - * позволяет выполнить дополнительные проверки. - * - * @return bool - */ - public function onAuth(): bool - { - return true; - } - - public static function passwdToDB(string $passwd): string - { - return $passwd; - } - - public static function verifyPassword(string $plainPassword, string $hash): bool - { - return static::passwdToDB($plainPassword) === $hash; - } + /** @var PXRequest */ + protected $request; + + /** @var PXDatabase */ + protected $db; + + /** @var PXApplication */ + protected $app; + + /** @var PXUser */ + protected $user; + + /** @var null|SessionInterface */ + protected $session; + + /** @var @var string|null */ + protected $login; + + /** @var @var string|null */ + protected $passwd; + + public function __construct(?array $params = []) + { + // params is not used right now.. + } + + /** + * {@inheritdoc} + */ + public function setRequest(PXRequest $request): AuthInterface + { + $this->request = $request; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDb(PXDatabase $db): AuthInterface + { + $this->db = $db; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setApp(PXApplication $app): AuthInterface + { + $this->app = $app; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setUser(PXUser $user): AuthInterface + { + $this->user = $user; + + return $this; + } + + /** + * @param null|SessionInterface $session + * @return $this + */ + public function setSession(?SessionInterface $session = null): AuthInterface + { + $this->session = $session; + + return $this; + } + + /** + * {@inheritdoc} + */ + abstract public function isCredentialsValid(array $credentials): bool; + + /** + * {@inheritdoc} + */ + abstract public function isAuthorized(): bool; + + public function fillUserFields(array $uArray): void + { + $user = $this->user ?: PXRegistry::getUser(); + + $user->id = $uArray['id'] ?? null; + $user->login = $uArray['title'] ?? null; + $user->data = $uArray; + $this->passwd = $user->passwd = $uArray['passwd'] ?? null; + } + + public function getTitle(): ?string + { + return $this->user->login; + } + + protected function findUser(): ?array + { + if (!mb_strlen((string) $this->login)) { + return null; + } + + $tmp = $this->db->getObjectsByFieldLimited( + $this->app->types[DT_USER], + true, + 'title', + $this->login, + 1, + 0 + ); + + return count($tmp) ? current($tmp) : null; + } + + public function auth(): bool + { + return true; + } + + public function unAuth(): bool + { + return true; + } + + /** + * Метод-триггер, вызывается в PXUser::checkAuth() после загрузки правил acl, + * позволяет выполнить дополнительные проверки. + * + * @return bool + */ + public function onAuth(): bool + { + return true; + } + + public static function passwdToDB(string $passwd): string + { + return $passwd; + } + + public static function verifyPassword(string $plainPassword, string $hash): bool + { + return static::passwdToDB($plainPassword) === $hash; + } } diff --git a/src/Lib/Auth/AuthException.php b/src/Lib/Auth/AuthException.php index d5b7aabf..39961eee 100644 --- a/src/Lib/Auth/AuthException.php +++ b/src/Lib/Auth/AuthException.php @@ -2,5 +2,6 @@ namespace PP\Lib\Auth; -class AuthException extends \Exception { +class AuthException extends \Exception +{ } diff --git a/src/Lib/Auth/AuthInterface.php b/src/Lib/Auth/AuthInterface.php index 0a2ada9f..1eb77567 100644 --- a/src/Lib/Auth/AuthInterface.php +++ b/src/Lib/Auth/AuthInterface.php @@ -4,80 +4,80 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface; -interface AuthInterface { - - /** - * @param array $credentials - * @return bool - */ - public function isCredentialsValid(array $credentials): bool; - - /** - * @return bool - */ - public function isAuthorized(): bool; - - /** - * @param array $uArray - * @return bool - */ - public function fillUserFields(array $uArray): void; - - /** - * @return bool - */ - public function auth(): bool; - - /** - * @return bool - */ - public function unAuth(): bool; - - /** - * @param \PXRequest $request - * @return $this - */ - public function setRequest(\PXRequest $request): self; - - /** - * @param \PXDatabase $db - * @return $this - */ - public function setDb(\PXDatabase $db): self; - - /** - * @param \PXApplication $app - * @return $this - */ - public function setApp(\PXApplication $app): self; - - /** - * @param \PXUser $user - * @return $this - */ - public function setUser(\PXUser $user): self; - - /** - * @param Session|null $session - * @return $this - */ - public function setSession(?SessionInterface $session = null): self; - - /** - * @return bool - */ - public function onAuth(): bool; - - /** - * @param string $passwd - * @return string - */ - public static function passwdToDB(string $passwd): string; - - /** - * @param string $plainPassword - * @param string $hash - * @return bool - */ - public static function verifyPassword(string $plainPassword, string $hash): bool; +interface AuthInterface +{ + /** + * @param array $credentials + * @return bool + */ + public function isCredentialsValid(array $credentials): bool; + + /** + * @return bool + */ + public function isAuthorized(): bool; + + /** + * @param array $uArray + * @return bool + */ + public function fillUserFields(array $uArray): void; + + /** + * @return bool + */ + public function auth(): bool; + + /** + * @return bool + */ + public function unAuth(): bool; + + /** + * @param \PXRequest $request + * @return $this + */ + public function setRequest(\PXRequest $request): self; + + /** + * @param \PXDatabase $db + * @return $this + */ + public function setDb(\PXDatabase $db): self; + + /** + * @param \PXApplication $app + * @return $this + */ + public function setApp(\PXApplication $app): self; + + /** + * @param \PXUser $user + * @return $this + */ + public function setUser(\PXUser $user): self; + + /** + * @param Session|null $session + * @return $this + */ + public function setSession(?SessionInterface $session = null): self; + + /** + * @return bool + */ + public function onAuth(): bool; + + /** + * @param string $passwd + * @return string + */ + public static function passwdToDB(string $passwd): string; + + /** + * @param string $plainPassword + * @param string $hash + * @return bool + */ + public static function verifyPassword(string $plainPassword, string $hash): bool; } diff --git a/src/Lib/Auth/NullAuth.php b/src/Lib/Auth/NullAuth.php index 9483b190..981b24f9 100644 --- a/src/Lib/Auth/NullAuth.php +++ b/src/Lib/Auth/NullAuth.php @@ -2,22 +2,22 @@ namespace PP\Lib\Auth; -class NullAuth extends AuthAbstract { +class NullAuth extends AuthAbstract +{ + /** + * {@inheritdoc} + */ + public function isAuthorized(): bool + { + return true; + } - /** - * {@inheritdoc} - */ - public function isAuthorized(): bool - { - return true; - } - - /** - * {@inheritdoc} - */ - public function isCredentialsValid(array $credentials): bool - { - return true; - } + /** + * {@inheritdoc} + */ + public function isCredentialsValid(array $credentials): bool + { + return true; + } } diff --git a/src/Lib/Auth/Session.php b/src/Lib/Auth/Session.php index 9c2afbb3..0a546990 100644 --- a/src/Lib/Auth/Session.php +++ b/src/Lib/Auth/Session.php @@ -6,73 +6,73 @@ class Session extends AuthAbstract { - public const AUTHORIZED_USER_ID = '__auth_user_id'; - public const AUTHORIZED_USER_IP = '__auth_user_ip'; - - public function isCredentialsValid(array $credentials): bool - { - $this->login = getFromArray($credentials, 'login'); - $this->passwd = getFromArray($credentials, 'password'); - - $uArray = $this->findUser(); - - if ($uArray && strlen($this->passwd) > 0 && static::verifyPassword($this->passwd, $uArray['passwd'])) { - $this->fillUserFields($uArray); - } - - return $this->user->id > 0; - } - - public function isAuthorized(): bool - { - // if no session opened, credentials are invalid - if (!($this->session instanceof SessionInterface)) { - return false; - } - - $userId = (int)$this->session->get(static::AUTHORIZED_USER_ID); - $userIp = (string)$this->session->get(static::AUTHORIZED_USER_IP); - - if ($userId > 0) { - if ($userIp !== $this->request->GetRemoteAddr()) { - $this->session->invalidate(); - return false; - } - - $uArray = $this->db->getObjectById($this->app->types[DT_USER], $userId); - - if (empty($uArray['status'])) { - $this->session->invalidate(); - return false; - } - - $this->fillUserFields($uArray); - } - - return $this->user->id > 0; - } - - public function auth(): bool - { - $this->session->set(static::AUTHORIZED_USER_ID, $this->user->id); - $this->session->set(static::AUTHORIZED_USER_IP, $this->request->GetRemoteAddr()); - $this->session->migrate(true); - return true; - } - - public function unAuth(): bool - { - $this->session->invalidate(); - return true; - } - - public static function passwdToDB(string $passwd): string - { - return password_hash($passwd, PASSWORD_BCRYPT); - } - - public static function verifyPassword(string $plainPassword, string $hash): bool - { - return password_verify($plainPassword, $hash); - } + public const AUTHORIZED_USER_ID = '__auth_user_id'; + public const AUTHORIZED_USER_IP = '__auth_user_ip'; + + public function isCredentialsValid(array $credentials): bool + { + $this->login = getFromArray($credentials, 'login'); + $this->passwd = getFromArray($credentials, 'password'); + + $uArray = $this->findUser(); + + if ($uArray && strlen((string) $this->passwd) > 0 && static::verifyPassword($this->passwd, $uArray['passwd'])) { + $this->fillUserFields($uArray); + } + + return $this->user->id > 0; + } + + public function isAuthorized(): bool + { + // if no session opened, credentials are invalid + if (!($this->session instanceof SessionInterface)) { + return false; + } + + $userId = (int)$this->session->get(static::AUTHORIZED_USER_ID); + $userIp = (string)$this->session->get(static::AUTHORIZED_USER_IP); + + if ($userId > 0) { + if ($userIp !== $this->request->GetRemoteAddr()) { + $this->session->invalidate(); + return false; + } + + $uArray = $this->db->getObjectById($this->app->types[DT_USER], $userId); + + if (empty($uArray['status'])) { + $this->session->invalidate(); + return false; + } + + $this->fillUserFields($uArray); + } + + return $this->user->id > 0; + } + + public function auth(): bool + { + $this->session->set(static::AUTHORIZED_USER_ID, $this->user->id); + $this->session->set(static::AUTHORIZED_USER_IP, $this->request->GetRemoteAddr()); + $this->session->migrate(true); + return true; + } + + public function unAuth(): bool + { + $this->session->invalidate(); + return true; + } + + public static function passwdToDB(string $passwd): string + { + return password_hash($passwd, PASSWORD_BCRYPT); + } + + public static function verifyPassword(string $plainPassword, string $hash): bool + { + return password_verify($plainPassword, $hash); + } } diff --git a/src/Lib/Cache/Driver/Apc.php b/src/Lib/Cache/Driver/Apc.php index b4bda275..d073d161 100644 --- a/src/Lib/Cache/Driver/Apc.php +++ b/src/Lib/Cache/Driver/Apc.php @@ -10,12 +10,12 @@ */ class Apc implements CacheInterface { - private $cacheDomain; - private $expirationTime; + private readonly string $cacheDomain; + private readonly int $expirationTime; public function __construct($cacheDomain = null, $defaultExpire = 3600) { - extension_loaded("apc") && ini_get("apc.enabled") or FatalError(get_class($this) . " error: APC extension disabled or doesn't installed"); + extension_loaded("apc") && ini_get("apc.enabled") or FatalError(static::class . " error: APC extension disabled or doesn't installed"); $this->expirationTime = (int)$defaultExpire; $this->cacheDomain = BASEPATH . $cacheDomain; } @@ -38,7 +38,7 @@ public function exists($key) public function save($key, $data, $expTime = null) { - apc_store($this->key($key), $data, ((int)$expTime ? (int)$expTime : $this->expirationTime)) or $this->clear(); + apc_store($this->key($key), $data, ((int)$expTime ?: $this->expirationTime)) or $this->clear(); } public function load($key) diff --git a/src/Lib/Cache/Driver/File.php b/src/Lib/Cache/Driver/File.php index f5c1d30a..5b41ba7a 100644 --- a/src/Lib/Cache/Driver/File.php +++ b/src/Lib/Cache/Driver/File.php @@ -13,238 +13,238 @@ */ class File implements CacheInterface, SerializerAwareInterface { - use SerializerAwareTrait; - - protected $cache_dir; - protected $expire; - protected $orderLevel = 0; - - public function __construct($cacheDomain = null, $defaultExpire = 3600) - { - $this->serializer = new DefaultSerializer(); - $this->cache_dir = CACHE_PATH . '/'; - - if ($cacheDomain !== null) { - $this->cache_dir .= $cacheDomain . '/'; - } - - $this->setExpire((int)$defaultExpire); - - $this->_createCacheDir(); - } - - public function getCacheDir() - { - return $this->cache_dir; - } - - public function setOrderLevel($level = 0) - { - $this->orderLevel = $level; - } - - /** @param int $expire - seconds */ - public function setExpire($expire) - { - $this->expire = $expire; - } - - public function exists($objectId) - { - $fileName = $this->_getFilename($objectId); - return file_exists($fileName); - } - - public function save($objectId, $data, $expTime = null) - { - $fileName = $this->_getFilename($objectId); - $serialized = $this->serializer->serialize($data); - $this->_doSave($fileName, $serialized, (int)$expTime); - } - - public function load($objectId) - { - $fileName = $this->_getFilename($objectId); - return $this->_doLoad($fileName); - } - - public function increment($numberKey, $offset = 1, $initial = 0, $expTime = null) - { - $fileName = $this->_getFilename($numberKey); - $fp = @fopen($fileName, 'a+b'); - - if ($fp !== false) { - if (flock($fp, LOCK_EX)) { - if (!$this->_isExpired($fp)) { // note: valid until 2038 ;) - $data = ''; - while (!feof($fp)) { - $data .= fread($fp, 8192); - } - } - isset($data) || $data = $initial; - $data += $offset; - ftruncate($fp, 0); - fseek($fp, 0, SEEK_SET); - fwrite($fp, (time() + ($expTime ? $expTime : $this->expire)) . $data); - flock($fp, LOCK_UN); - } - - fclose($fp); - } - if (!isset($data)) { - FatalError('Reading/writing file error "' . $fileName . '"'); - } - return isset($data) ? $data : $initial; - } - - public function loadStaled($objectId) - { - $fileName = $this->_getFilename($objectId); - return $this->_doLoad($fileName, 'expired'); - } - - public function delete($key) - { - $file = $this->_getFilename($key); - @unlink($file); - } - - public function expired($objectId) - { - $fileName = $this->_getFilename($objectId); - $fp = @fopen($fileName, 'rb'); - - $expired = true; - - if ($fp !== false) { - if (flock($fp, LOCK_SH)) { - $expired = $this->_isExpired($fp); - flock($fp, LOCK_UN); - } - - fclose($fp); - } - - return $expired; - } - - public function clear() - { - $this->_cleanDir($this->cache_dir . '/'); - } - - public function deleteGroup($gorup) - { - $fileGlob = $this->_getFilename($gorup, true); - $files = glob($fileGlob, GLOB_MARK | GLOB_NOSORT); - - foreach ($files as $file) { - if (pathinfo($file, PATHINFO_BASENAME) == '.' || pathinfo($file, PATHINFO_BASENAME) == '..') { - continue; - } - if (is_dir($file)) { - $this->_cleanDir($file . '/'); - } else { - @unlink($file); - } - } - } - - protected function _cleanDir($dirName) - { - if ($handle = opendir($dirName)) { - while (false !== ($file = readdir($handle))) { - if ($file == '.' || $file == '..') { - continue; - } - $file = $dirName . $file; - if (is_dir($file)) { - $this->_cleanDir($file . '/'); - } else { - @unlink($file); - } - } - - closedir($handle); - } - } - - protected function _getFilename($str, $glob = false) - { - if (is_array($str)) { - $key = array_shift($str); - $group = array_shift($str); - $md5 = md5($group) . md5($key); - } else { - $md5 = md5($str) . ($glob ? '*' : ''); - } - - if ($this->orderLevel) { - $prefix = explode('', $md5, $this->orderLevel + 1); - $md5 = array_pop($prefix); - $prefix = join('/', $prefix) . '/'; - - MakeDirIfNotExists($this->cache_dir . $prefix); - $md5 = $prefix . $md5; - } - - return $this->cache_dir . $md5; - } - - protected function _doSave($fileName, $serialized, $expTime = null) - { - $fp = fopen($fileName, 'wb'); - - if ($fp !== false) { - if (flock($fp, LOCK_EX)) { - fwrite($fp, (time() + ($expTime ? $expTime : $this->expire)) . $serialized); - flock($fp, LOCK_UN); - } - - fclose($fp); - } - } - - /** - * @return string - serialized data - */ - protected function _doLoad($fileName, $expired = false) - { - $unserialized = $serialized = null; - $fp = @fopen($fileName, 'rb'); - - if ($fp !== false) { - if (flock($fp, LOCK_SH)) { - if (!$this->_isExpired($fp) || $expired) { // note: valid until 2038 ;) - while (!feof($fp)) { - $serialized .= fread($fp, 8192); - } - } - flock($fp, LOCK_UN); - } - fclose($fp); - } - - // avoiding error on: unserialize(serialize(false)) - if ($serialized === 'b:0;') { - $unserialized = false; - } elseif ($serialized !== null) { - $tmp = $this->serializer->unserialize($serialized); - if ($tmp !== false) { - $unserialized = $tmp; - } - } - - return $unserialized; - } - - protected function _createCacheDir() - { - MakeDirIfNotExists($this->cache_dir); - } - - protected function _isExpired($fp) - { - return time() >= (int)fread($fp, 10); // note: valid until 2038 ;) - } + use SerializerAwareTrait; + + protected $cache_dir; + protected $expire; + protected $orderLevel = 0; + + public function __construct($cacheDomain = null, $defaultExpire = 3600) + { + $this->serializer = new DefaultSerializer(); + $this->cache_dir = CACHE_PATH . '/'; + + if ($cacheDomain !== null) { + $this->cache_dir .= $cacheDomain . '/'; + } + + $this->setExpire((int)$defaultExpire); + + $this->_createCacheDir(); + } + + public function getCacheDir() + { + return $this->cache_dir; + } + + public function setOrderLevel($level = 0) + { + $this->orderLevel = $level; + } + + /** @param int $expire - seconds */ + public function setExpire($expire) + { + $this->expire = $expire; + } + + public function exists($objectId) + { + $fileName = $this->_getFilename($objectId); + return file_exists($fileName); + } + + public function save($objectId, $data, $expTime = null) + { + $fileName = $this->_getFilename($objectId); + $serialized = $this->serializer->serialize($data); + $this->_doSave($fileName, $serialized, (int)$expTime); + } + + public function load($objectId) + { + $fileName = $this->_getFilename($objectId); + return $this->_doLoad($fileName); + } + + public function increment($numberKey, $offset = 1, $initial = 0, $expTime = null) + { + $fileName = $this->_getFilename($numberKey); + $fp = @fopen($fileName, 'a+b'); + + if ($fp !== false) { + if (flock($fp, LOCK_EX)) { + if (!$this->_isExpired($fp)) { // note: valid until 2038 ;) + $data = ''; + while (!feof($fp)) { + $data .= fread($fp, 8192); + } + } + isset($data) || $data = $initial; + $data += $offset; + ftruncate($fp, 0); + fseek($fp, 0, SEEK_SET); + fwrite($fp, (time() + ($expTime ?: $this->expire)) . $data); + flock($fp, LOCK_UN); + } + + fclose($fp); + } + if (!isset($data)) { + FatalError('Reading/writing file error "' . $fileName . '"'); + } + return $data ?? $initial; + } + + public function loadStaled($objectId) + { + $fileName = $this->_getFilename($objectId); + return $this->_doLoad($fileName, 'expired'); + } + + public function delete($key) + { + $file = $this->_getFilename($key); + @unlink($file); + } + + public function expired($objectId) + { + $fileName = $this->_getFilename($objectId); + $fp = @fopen($fileName, 'rb'); + + $expired = true; + + if ($fp !== false) { + if (flock($fp, LOCK_SH)) { + $expired = $this->_isExpired($fp); + flock($fp, LOCK_UN); + } + + fclose($fp); + } + + return $expired; + } + + public function clear() + { + $this->_cleanDir($this->cache_dir . '/'); + } + + public function deleteGroup($gorup) + { + $fileGlob = $this->_getFilename($gorup, true); + $files = glob($fileGlob, GLOB_MARK | GLOB_NOSORT); + + foreach ($files as $file) { + if (pathinfo((string) $file, PATHINFO_BASENAME) == '.' || pathinfo((string) $file, PATHINFO_BASENAME) == '..') { + continue; + } + if (is_dir($file)) { + $this->_cleanDir($file . '/'); + } else { + @unlink($file); + } + } + } + + protected function _cleanDir($dirName) + { + if ($handle = opendir($dirName)) { + while (false !== ($file = readdir($handle))) { + if ($file == '.' || $file == '..') { + continue; + } + $file = $dirName . $file; + if (is_dir($file)) { + $this->_cleanDir($file . '/'); + } else { + @unlink($file); + } + } + + closedir($handle); + } + } + + protected function _getFilename($str, $glob = false) + { + if (is_array($str)) { + $key = array_shift($str); + $group = array_shift($str); + $md5 = md5((string) $group) . md5((string) $key); + } else { + $md5 = md5((string) $str) . ($glob ? '*' : ''); + } + + if ($this->orderLevel) { + $prefix = explode('', $md5, $this->orderLevel + 1); + $md5 = array_pop($prefix); + $prefix = join('/', $prefix) . '/'; + + MakeDirIfNotExists($this->cache_dir . $prefix); + $md5 = $prefix . $md5; + } + + return $this->cache_dir . $md5; + } + + protected function _doSave($fileName, $serialized, $expTime = null) + { + $fp = fopen($fileName, 'wb'); + + if ($fp !== false) { + if (flock($fp, LOCK_EX)) { + fwrite($fp, (time() + ($expTime ?: $this->expire)) . $serialized); + flock($fp, LOCK_UN); + } + + fclose($fp); + } + } + + /** + * @return string - serialized data + */ + protected function _doLoad($fileName, $expired = false) + { + $unserialized = $serialized = null; + $fp = @fopen($fileName, 'rb'); + + if ($fp !== false) { + if (flock($fp, LOCK_SH)) { + if (!$this->_isExpired($fp) || $expired) { // note: valid until 2038 ;) + while (!feof($fp)) { + $serialized .= fread($fp, 8192); + } + } + flock($fp, LOCK_UN); + } + fclose($fp); + } + + // avoiding error on: unserialize(serialize(false)) + if ($serialized === 'b:0;') { + $unserialized = false; + } elseif ($serialized !== null) { + $tmp = $this->serializer->unserialize($serialized); + if ($tmp !== false) { + $unserialized = $tmp; + } + } + + return $unserialized; + } + + protected function _createCacheDir() + { + MakeDirIfNotExists($this->cache_dir); + } + + protected function _isExpired($fp) + { + return time() >= (int)fread($fp, 10); // note: valid until 2038 ;) + } } diff --git a/src/Lib/Cache/Driver/Memcached.php b/src/Lib/Cache/Driver/Memcached.php index 4f7cf8e9..7ca24843 100644 --- a/src/Lib/Cache/Driver/Memcached.php +++ b/src/Lib/Cache/Driver/Memcached.php @@ -10,130 +10,128 @@ */ class Memcached implements CacheInterface { - /** @var \Memcached */ - private $connection; - - /** @var int */ - private $defaultExpireTime; - - /** @var string */ - private $cacheNamespace; - - /** @var string */ - public $host = 'localhost'; - - /** @var int */ - public $port = 11211; - - public function __construct($cacheDomain = null, $defaultExpire = 3600, $connectorArgs = null) - { - if (!extension_loaded("memcached")) { - throw new \Exception("Memcached extension is not loaded"); - } - - $this->defaultExpireTime = (int)$defaultExpire; - $this->cacheNamespace = md5(BASEPATH . $cacheDomain); - $this->host = getFromArray($connectorArgs, 'host', $this->host); - $this->port = (int)getFromArray($connectorArgs, 'port', $this->port); - $this->connection = $this->connect(); - } - - public function exists($key) - { - $this->connection->get($this->key($key)); - return $this->connection->getResultCode() !== \Memcached::RES_NOTFOUND; - } - - public function save($key, $data, $expTime = null) - { - $expTime = (int)$expTime; - $expTime = ($expTime > 0) - ? $expTime - : $this->defaultExpireTime; - - $this->connection->set($this->key($key), $data, $expTime); - } - - public function load($key) - { - // look at that: https://github.com/php-memcached-dev/php-memcached/issues/21 - $res = $this->connection->get($this->key($key)); - return $this->connection->getResultCode() == \Memcached::RES_NOTFOUND ? null : $res; - } - - public function delete($key) - { - $this->connection->delete($this->key($key)); - } - - public function deleteGroup($group) - { - $prefix = $this->key($group, true); - $prefLen = mb_strlen($prefix); - $allKeys = $this->connection->getAllKeys(); - if (empty($allKeys)) { - return; - } - - $toDelete = []; - foreach ($allKeys as $key) { - if (mb_substr($key, 0, $prefLen) == $prefix) { - $toDelete[] = $key; - } - } - - if (!empty($toDelete)) { - $this->connection->deleteMulti($toDelete); - } - } - - public function increment($key, $offset = 1, $initial = 0, $expTime = null) - { - $expTime = (int)$expTime; - $expTime = ($expTime > 0) - ? $expTime - : $this->defaultExpireTime; - - $k = $this->key($key); - $this->connection->add($k, $initial, $expTime); - return $this->connection->increment($this->key($key), $offset, $initial, $expTime); - } - - public function clear() - { - $this->connection->flush(); - } - - private function connect() - { - // WARNING: Avoid persistent connections from cron tasks: task runner uses fork - // emulate persistent connection_id. - $mcObject = new \Memcached($this->cacheNamespace . getmypid()); - - if (!count($mcObject->getServerList())) { - // WARNING: persistent connection settings must be set only once! - $mcObject->setOptions([ - \Memcached::OPT_HASH => \Memcached::HASH_MURMUR, - \Memcached::OPT_BINARY_PROTOCOL => true, - \Memcached::OPT_PREFIX_KEY => $this->cacheNamespace, - \Memcached::OPT_TCP_NODELAY => true //for small data packets - ]); - - if (!$mcObject->addServer($this->host, $this->port)) { - throw new \Exception("Connection to memcached: {$this->host}:{$this->port} failed"); - } - } - - return $mcObject; - } - - private function key($key, $glob = false) - { - if (is_array($key)) { - $keyPart = $this->key(array_shift($key)); - $groupPart = $this->key(array_shift($key)); - return $groupPart . '_' . $keyPart; - } - return md5($key) . ($glob ? '_' : ''); - } + /** @var \Memcached */ + private $connection; + + private readonly int $defaultExpireTime; + + private readonly string $cacheNamespace; + + /** @var string */ + public $host = 'localhost'; + + /** @var int */ + public $port = 11211; + + public function __construct($cacheDomain = null, $defaultExpire = 3600, $connectorArgs = null) + { + if (!extension_loaded("memcached")) { + throw new \Exception("Memcached extension is not loaded"); + } + + $this->defaultExpireTime = (int)$defaultExpire; + $this->cacheNamespace = md5(BASEPATH . $cacheDomain); + $this->host = getFromArray($connectorArgs, 'host', $this->host); + $this->port = (int)getFromArray($connectorArgs, 'port', $this->port); + $this->connection = $this->connect(); + } + + public function exists($key) + { + $this->connection->get($this->key($key)); + return $this->connection->getResultCode() !== \Memcached::RES_NOTFOUND; + } + + public function save($key, $data, $expTime = null) + { + $expTime = (int)$expTime; + $expTime = ($expTime > 0) + ? $expTime + : $this->defaultExpireTime; + + $this->connection->set($this->key($key), $data, $expTime); + } + + public function load($key) + { + // look at that: https://github.com/php-memcached-dev/php-memcached/issues/21 + $res = $this->connection->get($this->key($key)); + return $this->connection->getResultCode() == \Memcached::RES_NOTFOUND ? null : $res; + } + + public function delete($key) + { + $this->connection->delete($this->key($key)); + } + + public function deleteGroup($group) + { + $prefix = $this->key($group, true); + $prefLen = mb_strlen((string) $prefix); + $allKeys = $this->connection->getAllKeys(); + if (empty($allKeys)) { + return; + } + + $toDelete = []; + foreach ($allKeys as $key) { + if (mb_substr((string) $key, 0, $prefLen) == $prefix) { + $toDelete[] = $key; + } + } + + if (!empty($toDelete)) { + $this->connection->deleteMulti($toDelete); + } + } + + public function increment($key, $offset = 1, $initial = 0, $expTime = null) + { + $expTime = (int)$expTime; + $expTime = ($expTime > 0) + ? $expTime + : $this->defaultExpireTime; + + $k = $this->key($key); + $this->connection->add($k, $initial, $expTime); + return $this->connection->increment($this->key($key), $offset, $initial, $expTime); + } + + public function clear() + { + $this->connection->flush(); + } + + private function connect() + { + // WARNING: Avoid persistent connections from cron tasks: task runner uses fork + // emulate persistent connection_id. + $mcObject = new \Memcached($this->cacheNamespace . getmypid()); + + if (!count($mcObject->getServerList())) { + // WARNING: persistent connection settings must be set only once! + $mcObject->setOptions([ + \Memcached::OPT_HASH => \Memcached::HASH_MURMUR, + \Memcached::OPT_BINARY_PROTOCOL => true, + \Memcached::OPT_PREFIX_KEY => $this->cacheNamespace, + \Memcached::OPT_TCP_NODELAY => true //for small data packets + ]); + + if (!$mcObject->addServer($this->host, $this->port)) { + throw new \Exception("Connection to memcached: {$this->host}:{$this->port} failed"); + } + } + + return $mcObject; + } + + private function key($key, $glob = false) + { + if (is_array($key)) { + $keyPart = $this->key(array_shift($key)); + $groupPart = $this->key(array_shift($key)); + return $groupPart . '_' . $keyPart; + } + return md5((string) $key) . ($glob ? '_' : ''); + } } diff --git a/src/Lib/Cache/Driver/Predis.php b/src/Lib/Cache/Driver/Predis.php index f294e046..5113cc7f 100644 --- a/src/Lib/Cache/Driver/Predis.php +++ b/src/Lib/Cache/Driver/Predis.php @@ -16,145 +16,157 @@ * * @package PP\Lib\Cache\Driver */ -class Predis implements CacheInterface, SerializerAwareInterface { - use SerializerAwareTrait; - - /** - * @var Client - */ - protected $client; - - /** - * @var string - */ - protected $cachePrefix = ''; - - /** - * @var int - * @see https://redis.io/commands/scan#the-count-option - */ - protected $scanDefault = 50; - - /** - * @return int - */ - public function getScanDefault() { - return $this->scanDefault; - } - - /** - * @param int $scanDefault - * @return $this - */ - public function setScanDefault($scanDefault) { - $this->scanDefault = $scanDefault; - return $this; - } - - /** - * Predis constructor. - * - * @param null $cacheDomain - * @param int $defaultExpire - * @param null $connectorArgs - */ - public function __construct($cacheDomain = null, $defaultExpire = 3600, $connectorArgs = null) { - $this->cachePrefix = $cacheDomain === null ? '' : $cacheDomain . ':'; - $connectorArgs = str_replace('predis', 'redis', $connectorArgs); - $this->serializer = new DefaultSerializer(); - $this->client = new Client($connectorArgs, [ - 'prefix' => $this->cachePrefix - ]); - } - - /** - * {@inheritdoc} - */ - public function exists($key) { - return $this->client->exists($this->key($key)) > 0; - } - - /** - * {@inheritdoc} - */ - public function save($key, $data, $expTime = 3600) { - $serialized = $this->serializer->serialize($data); - $result = $this->client->set($this->key($key), $serialized, 'ex', $expTime); - - return $result->getPayload() === 'OK'; - } - - /** - * {@inheritdoc} - */ - public function load($key) { - $data = $this->client->get($this->key($key)); - $unserialized = $this->serializer->unserialize($data); - $unserialized = $unserialized === false ? null : $unserialized; - - return $unserialized; - } - - /** - * {@inheritdoc} - */ - public function increment($key, $offset = 1, $initial = 0, $expTime = null) { - $key = $this->key($key); - - if ($this->client->exists($key) === 0) { - $this->client->set($key, $initial); - } - - return $this->client->incrby($key, $offset); - } - - /** - * {@inheritdoc} - */ - public function delete($key) { - return $this->client->del([$this->key($key)]) > 0; - } - - /** - * {@inheritdoc} - */ - public function clear() { - return $this->client->flushdb()->getPayload() === 'OK'; - } - - /** - * {@inheritdoc} - */ - public function deleteGroup($group) { - $keyGroup = $this->key($group, true); - $cachePrefixLen = strlen($this->cachePrefix); - $pattern = $this->cachePrefix . $keyGroup; - $keys = new Keyspace($this->client, $pattern, $this->scanDefault); - - foreach ($keys as $key) { - $prefixlessKey = substr($key, $cachePrefixLen); - $this->client->del([$prefixlessKey]); - } - - return true; - } - - /** - * Convert Proxima key into Redis key. - * - * @param $key - * @param bool $glob - * @return string - */ - private function key($key, $glob = false) { - if (is_array($key)) { - $keyPart = $this->key(array_shift($key)); - $groupPart = $this->key(array_shift($key)); - - return $groupPart . '_' . $keyPart; - } - - return md5($key) . ($glob ? '_*' : ''); - } +class Predis implements CacheInterface, SerializerAwareInterface +{ + use SerializerAwareTrait; + + /** + * @var Client + */ + protected $client; + + /** + * @var string + */ + protected $cachePrefix = ''; + + /** + * @var int + * @see https://redis.io/commands/scan#the-count-option + */ + protected $scanDefault = 50; + + /** + * @return int + */ + public function getScanDefault() + { + return $this->scanDefault; + } + + /** + * @param int $scanDefault + * @return $this + */ + public function setScanDefault($scanDefault) + { + $this->scanDefault = $scanDefault; + return $this; + } + + /** + * Predis constructor. + * + * @param null $cacheDomain + * @param int $defaultExpire + * @param null $connectorArgs + */ + public function __construct($cacheDomain = null, $defaultExpire = 3600, $connectorArgs = null) + { + $this->cachePrefix = $cacheDomain === null ? '' : $cacheDomain . ':'; + $connectorArgs = str_replace('predis', 'redis', $connectorArgs); + $this->serializer = new DefaultSerializer(); + $this->client = new Client($connectorArgs, [ + 'prefix' => $this->cachePrefix + ]); + } + + /** + * {@inheritdoc} + */ + public function exists($key) + { + return $this->client->exists($this->key($key)) > 0; + } + + /** + * {@inheritdoc} + */ + public function save($key, $data, $expTime = 3600) + { + $serialized = $this->serializer->serialize($data); + $result = $this->client->set($this->key($key), $serialized, 'ex', $expTime); + + return $result->getPayload() === 'OK'; + } + + /** + * {@inheritdoc} + */ + public function load($key) + { + $data = $this->client->get($this->key($key)); + $unserialized = $this->serializer->unserialize($data); + $unserialized = $unserialized === false ? null : $unserialized; + + return $unserialized; + } + + /** + * {@inheritdoc} + */ + public function increment($key, $offset = 1, $initial = 0, $expTime = null) + { + $key = $this->key($key); + + if ($this->client->exists($key) === 0) { + $this->client->set($key, $initial); + } + + return $this->client->incrby($key, $offset); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + return $this->client->del([$this->key($key)]) > 0; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->client->flushdb()->getPayload() === 'OK'; + } + + /** + * {@inheritdoc} + */ + public function deleteGroup($group) + { + $keyGroup = $this->key($group, true); + $cachePrefixLen = strlen($this->cachePrefix); + $pattern = $this->cachePrefix . $keyGroup; + $keys = new Keyspace($this->client, $pattern, $this->scanDefault); + + foreach ($keys as $key) { + $prefixlessKey = substr((string) $key, $cachePrefixLen); + $this->client->del([$prefixlessKey]); + } + + return true; + } + + /** + * Convert Proxima key into Redis key. + * + * @param $key + * @param bool $glob + * @return string + */ + private function key($key, $glob = false) + { + if (is_array($key)) { + $keyPart = $this->key(array_shift($key)); + $groupPart = $this->key(array_shift($key)); + + return $groupPart . '_' . $keyPart; + } + + return md5((string) $key) . ($glob ? '_*' : ''); + } } diff --git a/src/Lib/Cache/Driver/Redis.php b/src/Lib/Cache/Driver/Redis.php index aae1a243..1c6ed1c9 100644 --- a/src/Lib/Cache/Driver/Redis.php +++ b/src/Lib/Cache/Driver/Redis.php @@ -20,156 +20,167 @@ * * connection timeout is set to 2.0 seconds (float) * */ -class Redis implements CacheInterface, SerializerAwareInterface { - use SerializerAwareTrait; - - /** @var RedisDriver */ - protected $connection; - - /** @var string */ - protected $cachePrefix = ''; - - /** @var string */ - protected $host; - - /** @var int */ - protected $port; - - /** @var float connection timeout, default is 1.5 */ - protected $timeout; - - /** @var int database number */ - protected $database = 0; - - /** - * Redis cache driver constructor. - * - * @param null|string $cacheDomain - * @param int $defaultExpire - * @param null|array $connectorArgs - */ - public function __construct($cacheDomain = null, $defaultExpire = 3600, $connectorArgs = null) { - if (!extension_loaded('redis')) { - FatalError('Redis extension is not loaded!'); - } - - // parse additional arguments.. - $paramsRaw = getFromArray($connectorArgs, 'query', ''); - parse_str($paramsRaw, $params); - - // create connection.. - $this->host = getFromArray($connectorArgs, 'host', '127.0.0.1'); - $this->port = getFromArray($connectorArgs, 'port', 6379); - $this->database = empty($connectorArgs['path']) ? $this->database : intval(ltrim($connectorArgs['path'], '/')); - $this->cachePrefix = ($cacheDomain === null) ? '' : $cacheDomain . ':'; - $this->timeout = (float)getFromArray($params, 'timeout', 1.5); - $this->serializer = new DefaultSerializer(); - $this->connect(); - } - - /** - * Initiate Redis non-persistent connection. - */ - private function connect() { - $this->connection = new RedisDriver(); - $this->connection->connect( - $this->host, - $this->port, - $this->timeout - ); - - if (!empty($this->cachePrefix)) { - $this->connection->setOption( - RedisDriver::OPT_PREFIX, - $this->cachePrefix - ); - } - $this->connection->select($this->database); - } - - /** - * Convert Proxima key into Redis key. - * - * @param string $key - * @param bool $glob - * @return string - */ - private function key($key, $glob = false) { - if (is_array($key)) { - $keyPart = $this->key(array_shift($key)); - $groupPart = $this->key(array_shift($key)); - return $groupPart . '_' . $keyPart; - } - return md5($key) . ($glob ? '_*' : ''); - } - - /** - * {@inheritdoc} - */ - public function exists($key) { - return $this->connection->exists($this->key($key)); - } - - /** - * {@inheritdoc} - */ - public function save($key, $data, $expTime = 3600) { - $serialized = $this->serializer->serialize($data); - return $this->connection->set($this->key($key), $serialized, $expTime); - } - - /** - * {@inheritdoc} - */ - public function load($key) { - $data = $this->connection->get($this->key($key)); - $unserialized = $this->serializer->unserialize($data); - $unserialized = $unserialized === false ? null : $data; - - return $unserialized; - } - - /** - * {@inheritdoc} - */ - public function increment($key, $offset = 1, $initial = 0, $expTime = null) { - $key = $this->key($key); - if (!$this->connection->exists($key)) { - $this->connection->set($key, $initial); - } - - return $this->connection->incrBy($key, $offset); - } - - /** - * {@inheritdoc} - */ - public function delete($key) { - return $this->connection->delete($this->key($key)); - } - - public function clear() { - return $this->connection->flushDB(); - } - - /** - * {@inheritdoc} - * - * @see https://github.com/phpredis/phpredis/issues/1117 - */ - public function deleteGroup($group) { - // find keys to delete - $keyGroup = $this->key($group, true); - $keys = $this->connection->keys($keyGroup); - $cachePrefixLen = strlen($this->cachePrefix); - - foreach ($keys as $key) { - if (substr($key, 0, $cachePrefixLen) === $this->cachePrefix) { - $key = substr($key, $cachePrefixLen); - $this->connection->del($key); - } - } - - return true; - } +class Redis implements CacheInterface, SerializerAwareInterface +{ + use SerializerAwareTrait; + + /** @var RedisDriver */ + protected $connection; + + /** @var string */ + protected $cachePrefix = ''; + + /** @var string */ + protected $host; + + /** @var int */ + protected $port; + + /** @var float connection timeout, default is 1.5 */ + protected $timeout; + + /** @var int database number */ + protected $database = 0; + + /** + * Redis cache driver constructor. + * + * @param null|string $cacheDomain + * @param int $defaultExpire + * @param null|array $connectorArgs + */ + public function __construct($cacheDomain = null, $defaultExpire = 3600, $connectorArgs = null) + { + if (!extension_loaded('redis')) { + FatalError('Redis extension is not loaded!'); + } + + // parse additional arguments.. + $paramsRaw = getFromArray($connectorArgs, 'query', ''); + parse_str((string) $paramsRaw, $params); + + // create connection.. + $this->host = getFromArray($connectorArgs, 'host', '127.0.0.1'); + $this->port = getFromArray($connectorArgs, 'port', 6379); + $this->database = empty($connectorArgs['path']) ? $this->database : intval(ltrim((string) $connectorArgs['path'], '/')); + $this->cachePrefix = ($cacheDomain === null) ? '' : $cacheDomain . ':'; + $this->timeout = (float)getFromArray($params, 'timeout', 1.5); + $this->serializer = new DefaultSerializer(); + $this->connect(); + } + + /** + * Initiate Redis non-persistent connection. + */ + private function connect() + { + $this->connection = new RedisDriver(); + $this->connection->connect( + $this->host, + $this->port, + $this->timeout + ); + + if (!empty($this->cachePrefix)) { + $this->connection->setOption( + RedisDriver::OPT_PREFIX, + $this->cachePrefix + ); + } + $this->connection->select($this->database); + } + + /** + * Convert Proxima key into Redis key. + * + * @param string $key + * @param bool $glob + * @return string + */ + private function key($key, $glob = false) + { + if (is_array($key)) { + $keyPart = $this->key(array_shift($key)); + $groupPart = $this->key(array_shift($key)); + return $groupPart . '_' . $keyPart; + } + return md5($key) . ($glob ? '_*' : ''); + } + + /** + * {@inheritdoc} + */ + public function exists($key) + { + return $this->connection->exists($this->key($key)); + } + + /** + * {@inheritdoc} + */ + public function save($key, $data, $expTime = 3600) + { + $serialized = $this->serializer->serialize($data); + return $this->connection->set($this->key($key), $serialized, $expTime); + } + + /** + * {@inheritdoc} + */ + public function load($key) + { + $data = $this->connection->get($this->key($key)); + $unserialized = $this->serializer->unserialize($data); + $unserialized = $unserialized === false ? null : $data; + + return $unserialized; + } + + /** + * {@inheritdoc} + */ + public function increment($key, $offset = 1, $initial = 0, $expTime = null) + { + $key = $this->key($key); + if (!$this->connection->exists($key)) { + $this->connection->set($key, $initial); + } + + return $this->connection->incrBy($key, $offset); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + return $this->connection->delete($this->key($key)); + } + + public function clear() + { + return $this->connection->flushDB(); + } + + /** + * {@inheritdoc} + * + * @see https://github.com/phpredis/phpredis/issues/1117 + */ + public function deleteGroup($group) + { + // find keys to delete + $keyGroup = $this->key($group, true); + $keys = $this->connection->keys($keyGroup); + $cachePrefixLen = strlen($this->cachePrefix); + + foreach ($keys as $key) { + if (substr($key, 0, $cachePrefixLen) === $this->cachePrefix) { + $key = substr($key, $cachePrefixLen); + $this->connection->del($key); + } + } + + return true; + } } diff --git a/src/Lib/Cache/ObjectCache.php b/src/Lib/Cache/ObjectCache.php index 54488740..a364a281 100644 --- a/src/Lib/Cache/ObjectCache.php +++ b/src/Lib/Cache/ObjectCache.php @@ -1,6 +1,7 @@ setSerializer($serializer); - } + $serializerDriver = getFromArray($params, 'serializer', 'default'); + $serializer = SerializerFactory::create($serializerDriver); + $instance->setSerializer($serializer); + } - if (!$instance instanceof CacheInterface) { - FatalError("Caching method:'{$cache}' - doesn't follow CacheInterface"); - } + if (!$instance instanceof CacheInterface) { + FatalError("Caching method:'{$cache}' - doesn't follow CacheInterface"); + } - return $instance; - } + return $instance; + } } diff --git a/src/Lib/Collection.php b/src/Lib/Collection.php index 3c6bbd3a..04575e87 100644 --- a/src/Lib/Collection.php +++ b/src/Lib/Collection.php @@ -12,130 +12,135 @@ * Class Collection * @package PP\Lib */ -class Collection implements Countable, IteratorAggregate, JsonSerializable { - - /** - * @var array - */ - protected $elements = []; - - /** - * Collection constructor. - * @param array $elements - */ - public function __construct(array $elements = []) { - $this->elements = $elements; - } - - /** - * @return array - */ - public function toArray() { - return $this->elements; - } - - /** - * @return bool - */ - public function isEmpty() { - return empty($this->elements); - } - - /** - * @return int - */ - public function count() { - return count($this->elements); - } - - /** - * @param Closure $func - * @return array - */ - public function map(Closure $func) { - return array_map($func, $this->elements); - } - - /** - * @param Closure $func - * @return static - */ - public function filter(Closure $func) { - return new static(array_filter($this->elements, $func)); - } - - /** - * @return mixed - */ - public function first() { - return reset($this->elements); - } - - /** - * @return mixed - */ - public function last() { - return end($this->elements); - } - - /** - * @return ArrayIterator - */ - public function getIterator() { - return new ArrayIterator($this->elements); - } - - /** - * @param mixed $key - * @return bool - */ - public function containsKey($key) { - return array_key_exists($key, $this->elements); - } - - /** - * @param mixed $key - * @param mixed $default - * @return mixed - */ - public function get($key, $default = null) { - if ($this->containsKey($key)) { - return $this->elements[$key]; - } - - return $default; - } - - /** - * @param mixed $key - * @param mixed $value - * @return Collection - */ - public function set($key, $value) { - if (is_null($key)) { - $this->elements[] = $value; - } else { - $this->elements[$key] = $value; - } - - return $this; - } - - /** - * @param mixed $value - * @return Collection - */ - public function push($value) { - $this->set(null, $value); - - return $this; - } - - /** - * @inheritdoc - */ - public function jsonSerialize() { - return $this->toArray(); - } +class Collection implements Countable, IteratorAggregate, JsonSerializable +{ + /** + * @var array + */ + protected $elements = []; + + /** + * Collection constructor. + */ + public function __construct(array $elements = []) + { + $this->elements = $elements; + } + + /** + * @return array + */ + public function toArray() + { + return $this->elements; + } + + /** + * @return bool + */ + public function isEmpty() + { + return empty($this->elements); + } + + /** + * @return int + */ + public function count() + { + return count($this->elements); + } + + /** + * @return array + */ + public function map(Closure $func) + { + return array_map($func, $this->elements); + } + + /** + * @return static + */ + public function filter(Closure $func) + { + return new static(array_filter($this->elements, $func)); + } + + /** + * @return mixed + */ + public function first() + { + return reset($this->elements); + } + + /** + * @return mixed + */ + public function last() + { + return end($this->elements); + } + + /** + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->elements); + } + + /** + * @return bool + */ + public function containsKey(mixed $key) + { + return array_key_exists($key, $this->elements); + } + + /** + * @return mixed + */ + public function get(mixed $key, mixed $default = null) + { + if ($this->containsKey($key)) { + return $this->elements[$key]; + } + + return $default; + } + + /** + * @return Collection + */ + public function set(mixed $key, mixed $value) + { + if (is_null($key)) { + $this->elements[] = $value; + } else { + $this->elements[$key] = $value; + } + + return $this; + } + + /** + * @return Collection + */ + public function push(mixed $value) + { + $this->set(null, $value); + + return $this; + } + + /** + * @inheritdoc + */ + public function jsonSerialize() + { + return $this->toArray(); + } } diff --git a/src/Lib/Command/AbstractBasicCommand.php b/src/Lib/Command/AbstractBasicCommand.php index 91f15d2f..bf917d41 100644 --- a/src/Lib/Command/AbstractBasicCommand.php +++ b/src/Lib/Command/AbstractBasicCommand.php @@ -10,5 +10,6 @@ * * @package PP\Lib\Command */ -abstract class AbstractBasicCommand extends Command { +abstract class AbstractBasicCommand extends Command +{ } diff --git a/src/Lib/Command/AbstractCommand.php b/src/Lib/Command/AbstractCommand.php index e1e04496..9a6d2212 100644 --- a/src/Lib/Command/AbstractCommand.php +++ b/src/Lib/Command/AbstractCommand.php @@ -10,34 +10,34 @@ * Class AbstractCommand * @package PP\Lib\Command */ -abstract class AbstractCommand extends AbstractBasicCommand implements ContainerAwareInterface { - - use ContainerAwareTrait; - - /** @var \PXApplication */ - protected $app; - - /** @var \PXDatabase|PostgreSqlDriver */ - protected $db; - - /** - * @param \PXApplication $app - * @return $this - */ - public function setApp(\PXApplication $app) { - $this->app = $app; - - return $this; - } - - /** - * @param \PXDatabase $db - * @return $this - */ - public function setDb(\PXDatabase $db) { - $this->db = $db; - - return $this; - } +abstract class AbstractCommand extends AbstractBasicCommand implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + /** @var \PXApplication */ + protected $app; + + /** @var \PXDatabase|PostgreSqlDriver */ + protected $db; + + /** + * @return $this + */ + public function setApp(\PXApplication $app) + { + $this->app = $app; + + return $this; + } + + /** + * @return $this + */ + public function setDb(\PXDatabase $db) + { + $this->db = $db; + + return $this; + } } diff --git a/src/Lib/Command/MigrateAbstractCommand.php b/src/Lib/Command/MigrateAbstractCommand.php index 45867fc5..96e30f07 100644 --- a/src/Lib/Command/MigrateAbstractCommand.php +++ b/src/Lib/Command/MigrateAbstractCommand.php @@ -8,15 +8,15 @@ * Class MigrateAbstractCommand * @package PP\Command */ -abstract class MigrateAbstractCommand extends AbstractBasicCommand { +abstract class MigrateAbstractCommand extends AbstractBasicCommand +{ + /** @var PostgreSqlDriver */ + protected $dbDriver; - /** @var PostgreSqlDriver */ - protected $dbDriver; + protected $namespace = 'PP\Migration'; + protected $classPart = 'Migration'; - protected $namespace = 'PP\Migration'; - protected $classPart = 'Migration'; - - protected $template = <<dbDriver = $dbDriver; - $this->dbDriver->Connect(); - - return $this; - } - - /** - * @return string - */ - protected function getMigrationsDirectory() { - return APPPATH . '/migrations'; - } - - /** - * Return filename list of pending migrations. - * Query should run without any cache. - * - * @return string[] - * @throws \Exception - */ - protected function getPendingMigrations() { - $applied = $this->dbDriver->Query("SELECT * FROM _migrations", true); - if (!is_array($applied)) { - throw new \Exception("migrate:up failed, invalid query"); - } - - $applied = array_map(function ($item) { - return $item['filename']; - }, $applied); - - $dir = new \DirectoryIterator($this->getMigrationsDirectory()); - - $full = []; - foreach ($dir as $fileName) { - if ($fileName->getExtension() !== 'php') { - continue; - } - - $full[] = $fileName->getBasename(); - } - - $finalResult = array_diff($full, $applied); - sort($finalResult); - - return $finalResult; - } - - /** - * @param string $fileName - * @return string - */ - protected function getMigrationSqlFinalizer($fileName) { - return "INSERT INTO _migrations (filename) VALUES ('${fileName}')"; - } - - /** - * @param string $fileName - * @return string - */ - protected function getMigrationClass($fileName) { - $version = basename($fileName, '.php'); - return $this->classPart . $version; - } - - /** - * @param string $fileName - * @return string - */ - protected function getMigrationClassWithNamespace($fileName) { - return $this->namespace . '\\' . $this->getMigrationClass($fileName); - } + /** + * @param PostgreSqlDriver $dbDriver + * @return $this + */ + public function setDbDriver($dbDriver) + { + $this->dbDriver = $dbDriver; + $this->dbDriver->Connect(); + + return $this; + } + + /** + * @return string + */ + protected function getMigrationsDirectory() + { + return APPPATH . '/migrations'; + } + + /** + * Return filename list of pending migrations. + * Query should run without any cache. + * + * @return string[] + * @throws \Exception + */ + protected function getPendingMigrations() + { + $applied = $this->dbDriver->Query("SELECT * FROM _migrations", true); + if (!is_array($applied)) { + throw new \Exception("migrate:up failed, invalid query"); + } + + $applied = array_map(fn ($item) => $item['filename'], $applied); + + $dir = new \DirectoryIterator($this->getMigrationsDirectory()); + + $full = []; + foreach ($dir as $fileName) { + if ($fileName->getExtension() !== 'php') { + continue; + } + + $full[] = $fileName->getBasename(); + } + + $finalResult = array_diff($full, $applied); + sort($finalResult); + + return $finalResult; + } + + /** + * @param string $fileName + * @return string + */ + protected function getMigrationSqlFinalizer($fileName) + { + return "INSERT INTO _migrations (filename) VALUES ('${fileName}')"; + } + + /** + * @param string $fileName + * @return string + */ + protected function getMigrationClass($fileName) + { + $version = basename($fileName, '.php'); + return $this->classPart . $version; + } + + /** + * @param string $fileName + * @return string + */ + protected function getMigrationClassWithNamespace($fileName) + { + return $this->namespace . '\\' . $this->getMigrationClass($fileName); + } } diff --git a/src/Lib/Config/ApplicationInterface.php b/src/Lib/Config/ApplicationInterface.php new file mode 100644 index 00000000..3f378430 --- /dev/null +++ b/src/Lib/Config/ApplicationInterface.php @@ -0,0 +1,27 @@ +buffer .= join($sep, $cleanMessages) . $sep; - - parent::write($messages, $newline, $options); - } - - /** - * Fetches buffer and clears it. - * - * @return string - */ - public function fetch() { - $result = $this->buffer; - $this->buffer = ''; - - return $result; - } +class BuferringOutput extends ConsoleOutput +{ + /** @var string */ + protected $buffer = ''; + + /** + * @inheritdoc + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + $messages = (array) $messages; + + $cleanMessages = array_map('strip_tags', $messages); + $sep = $newline ? "\n" : ''; + + $this->buffer .= join($sep, $cleanMessages) . $sep; + + parent::write($messages, $newline, $options); + } + + /** + * Fetches buffer and clears it. + * + * @return string + */ + public function fetch() + { + $result = $this->buffer; + $this->buffer = ''; + + return $result; + } } diff --git a/src/Lib/Console/Report/Mailer.php b/src/Lib/Console/Report/Mailer.php index d7db0b89..e8626bab 100644 --- a/src/Lib/Console/Report/Mailer.php +++ b/src/Lib/Console/Report/Mailer.php @@ -9,171 +9,183 @@ * Class Mailer * @package PP\Lib\Console\Report */ -class Mailer { - - /** @var string */ - protected $to = ''; - - /** @var string */ - protected $from = ''; - - /** @var string */ - protected $commandName = 'example:command'; - - /** @var string */ - protected $projectName = 'example'; - - /** @var array */ - protected $options = []; - - /** - * Set list of e-mails. - * - * @param string $to - list of emails separated with comma - * @return $this - */ - public function setTo($to) { - $this->to = $to; - - return $this; - } - - /** - * @return string - */ - public function getTo() { - return $this->to; - } - - /** - * Sets e-mail field from. - * - * @param string $from - * @return $this - */ - public function setFrom($from) { - $this->from = $from; - - return $this; - } - - /** - * @return string - */ - public function getFrom() { - return $this->from; - } - - /** - * @param string $name - * @return $this - */ - public function setCommandName($name) { - $this->commandName = $name; - - return $this; - } - - /** - * @return string - */ - public function getCommandName() { - return $this->commandName; - } - - /** - * @param string $name - * @return $this - */ - public function setProjectName($name) { - $this->projectName = $name; - - return $this; - } - - /** - * @return string - */ - public function getProjectName() { - return $this->projectName; - } - - /** - * @param array $options - * @return $this - */ - public function setOptions($options) { - $this->options = $options; - - return $this; - } - - /** - * @return array - */ - public function getOptions() { - return $this->options; - } - - /** - * Sends result to mail addresses. - * - * @param string $data - * @return bool - * @throws Exception - */ - public function sendReport($data = '') { - $address = $this->getTo(); - if (!$address) { - throw new Exception('Empty email list.'); - } - - if (!$this->getFrom()) { - throw new Exception('Empty from field.'); - } - - $project = $this->getProjectName(); - $command = $this->getCommandName(); - - $mail = new NLMailMessage(); - $mail->setSubject(sprintf('%s PP %s script results', $project, $command)); - $mail->setFrom(sprintf('%s PP', $project), $this->getFrom()); - $mail->setBody($this->formatResultMailBody()); - - if ($data) { - $date = date('y:m:d_H:m:s'); - $fileName = sprintf('%s_%s_%s_report.txt', $date, $project, $command); - $fileName = str_replace(':', '-', $fileName); - $mail->addFile($fileName, basename($fileName), 'text/plain', $data); - } - - $addresses = explode(',', $address); - $to = array_shift($addresses); - $cc = join(',', $addresses); - - if ($cc) { - $mail->setCC($cc, false); - } - - $mail->setTo($to, $to); - return $mail->send(); - } - - /** - * Formats usual result mail body. - * - * @return string - */ - public function formatResultMailBody() { - $finishFormat = '%s PP %s script finished evaluation'; - $body = [ - sprintf($finishFormat, $this->getProjectName(), $this->getCommandName()), - '', - 'Used options:', - print_r($this->getOptions(), true) - ]; - - return join("\n", $body); - } +class Mailer +{ + /** @var string */ + protected $to = ''; + + /** @var string */ + protected $from = ''; + + /** @var string */ + protected $commandName = 'example:command'; + + /** @var string */ + protected $projectName = 'example'; + + /** @var array */ + protected $options = []; + + /** + * Set list of e-mails. + * + * @param string $to - list of emails separated with comma + * @return $this + */ + public function setTo($to) + { + $this->to = $to; + + return $this; + } + + /** + * @return string + */ + public function getTo() + { + return $this->to; + } + + /** + * Sets e-mail field from. + * + * @param string $from + * @return $this + */ + public function setFrom($from) + { + $this->from = $from; + + return $this; + } + + /** + * @return string + */ + public function getFrom() + { + return $this->from; + } + + /** + * @param string $name + * @return $this + */ + public function setCommandName($name) + { + $this->commandName = $name; + + return $this; + } + + /** + * @return string + */ + public function getCommandName() + { + return $this->commandName; + } + + /** + * @param string $name + * @return $this + */ + public function setProjectName($name) + { + $this->projectName = $name; + + return $this; + } + + /** + * @return string + */ + public function getProjectName() + { + return $this->projectName; + } + + /** + * @param array $options + * @return $this + */ + public function setOptions($options) + { + $this->options = $options; + + return $this; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * Sends result to mail addresses. + * + * @param string $data + * @return bool + * @throws Exception + */ + public function sendReport($data = '') + { + $address = $this->getTo(); + if (!$address) { + throw new Exception('Empty email list.'); + } + + if (!$this->getFrom()) { + throw new Exception('Empty from field.'); + } + + $project = $this->getProjectName(); + $command = $this->getCommandName(); + + $mail = new NLMailMessage(); + $mail->setSubject(sprintf('%s PP %s script results', $project, $command)); + $mail->setFrom(sprintf('%s PP', $project), $this->getFrom()); + $mail->setBody($this->formatResultMailBody()); + + if ($data) { + $date = date('y:m:d_H:m:s'); + $fileName = sprintf('%s_%s_%s_report.txt', $date, $project, $command); + $fileName = str_replace(':', '-', $fileName); + $mail->addFile($fileName, basename($fileName), 'text/plain', $data); + } + + $addresses = explode(',', $address); + $to = array_shift($addresses); + $cc = join(',', $addresses); + + if ($cc) { + $mail->setCC($cc, false); + } + + $mail->setTo($to, $to); + return $mail->send(); + } + + /** + * Formats usual result mail body. + * + * @return string + */ + public function formatResultMailBody() + { + $finishFormat = '%s PP %s script finished evaluation'; + $body = [ + sprintf($finishFormat, $this->getProjectName(), $this->getCommandName()), + '', + 'Used options:', + print_r($this->getOptions(), true) + ]; + + return join("\n", $body); + } } diff --git a/src/Lib/Database/AbstractSqlDatabase.php b/src/Lib/Database/AbstractSqlDatabase.php index b9dd05ab..9ab69a56 100644 --- a/src/Lib/Database/AbstractSqlDatabase.php +++ b/src/Lib/Database/AbstractSqlDatabase.php @@ -10,177 +10,176 @@ */ class AbstractSqlDatabase { - - /** @var ObjectCache */ - public $cache = null; - - protected $connectArray = [ - 'host' => 'localhost', - 'user' => '', - 'port' => '', - 'password' => '', - 'dbname' => 'test', - 'encoding' => DEFAULT_CHARSET, - ]; - - public function __construct(\NLDBDescription $dbDescription) - { - } - - public function connect() - { - } - - public function close() - { - } - - public function setCache($cacheType) - { - $this->cache = ObjectCache::get($cacheType, 'database'); - } - - public function modifyingQuery($query, $table = null, $retField = null, $flushCache = true, $retCount = false) - { - } - - public function modifyingCopy($tableName, $cols, $data) - { - } - - public function query($query, $donotusecache = false, $limitpair = null) - { - } - - public function insertObject($table, $fields, $values) - { - } - - public function limitOffsetString($limit, $offset) - { - } - - public function trueStatusString($status = 'TRUE') - { - return ($status == 'TRUE' || $status == 1) ? 'TRUE' : 'FALSE'; - } - - public function updateObjectById($table, $objectId, $fields, $values) - { - } - - public function dateTimeString($string) - { - } - - public function isUniqueColsCombination($tables, $colValues, $exception) - { - } - - public function getTableInfo($tableName) - { - return []; - } - - public function getError() - { - return "Error!"; - } - - public function EscapeString($string) - { - return addslashes($string); - } - - public function mapFields($field) - { - return $field; - } - - public function exportFloat($value) - { - return $value; - } - - public function exportDateTime($value) - { - return $value; - } - - public function vacuumTable($tableName) - { - } - - public function tableExists($tableName) - { - return true; - } - - public function transactionBegin() - { - } - - public function transactionCommit() - { - } - - public function transactionRollback() - { - } - - public function savepointCreate($name) - { - } - - public function savepointRelease($name) - { - } - - public function savepointRollbackTo($name) - { - } - - public function LIKE($condition, $percs) - { - return $this->_searchMethod("LIKE", $condition, $percs); - } - - public function inArray($arrayField, $value, $sane = false) - { - } - - public function intersectIntArray($arrayField, $values) - { - } - - public function _searchMethod($meth, $condition, $percs) - { - $lperc = P_LEFT & $percs ? '%' : ''; - $rperc = P_RIGHT & $percs ? '%' : ''; - return " " . $meth . " '" . $lperc . $this->EscapeString($condition) . $rperc . "' "; - } - - public function _loadFromCache($query, $customCacheExpiration) - { - if (!$customCacheExpiration || (is_int($customCacheExpiration) && $customCacheExpiration > 0)) { - $data = $this->cache->load($query); - if ($data !== null) { - return $data; - } - } - } - - public function _saveToCache($query, $data, $customCacheExpiration) - { - if (!$customCacheExpiration) { - $this->cache->save($query, $data); - } elseif (is_int($customCacheExpiration) && $customCacheExpiration > 0) { - $this->cache->save($query, $data, $customCacheExpiration); - } - } - - public function loggerSqlFormat($table, $fields) - { - - } + /** @var ObjectCache */ + public $cache = null; + + protected $connectArray = [ + 'host' => 'localhost', + 'user' => '', + 'port' => '', + 'password' => '', + 'dbname' => 'test', + 'encoding' => DEFAULT_CHARSET, + ]; + + public function __construct(\NLDBDescription $dbDescription) + { + } + + public function connect() + { + } + + public function close() + { + } + + public function setCache($cacheType) + { + $this->cache = ObjectCache::get($cacheType, 'database'); + } + + public function modifyingQuery($query, $table = null, $retField = null, $flushCache = true, $retCount = false) + { + } + + public function modifyingCopy($tableName, $cols, $data) + { + } + + public function query($query, $donotusecache = false, $limitpair = null) + { + } + + public function insertObject($table, $fields, $values) + { + } + + public function limitOffsetString($limit, $offset) + { + } + + public function trueStatusString($status = 'TRUE') + { + return ($status == 'TRUE' || $status == 1) ? 'TRUE' : 'FALSE'; + } + + public function updateObjectById($table, $objectId, $fields, $values) + { + } + + public function dateTimeString($string) + { + } + + public function isUniqueColsCombination($tables, $colValues, $exception) + { + } + + public function getTableInfo($tableName) + { + return []; + } + + public function getError() + { + return "Error!"; + } + + public function EscapeString($string) + { + return addslashes((string) $string); + } + + public function mapFields($field) + { + return $field; + } + + public function exportFloat($value) + { + return $value; + } + + public function exportDateTime($value) + { + return $value; + } + + public function vacuumTable($tableName) + { + } + + public function tableExists($tableName) + { + return true; + } + + public function transactionBegin() + { + } + + public function transactionCommit() + { + } + + public function transactionRollback() + { + } + + public function savepointCreate($name) + { + } + + public function savepointRelease($name) + { + } + + public function savepointRollbackTo($name) + { + } + + public function LIKE($condition, $percs) + { + return $this->_searchMethod("LIKE", $condition, $percs); + } + + public function inArray($arrayField, $value, $sane = false) + { + } + + public function intersectIntArray($arrayField, $values) + { + } + + public function _searchMethod($meth, $condition, $percs) + { + $lperc = P_LEFT & $percs ? '%' : ''; + $rperc = P_RIGHT & $percs ? '%' : ''; + return " " . $meth . " '" . $lperc . $this->EscapeString($condition) . $rperc . "' "; + } + + public function _loadFromCache($query, $customCacheExpiration) + { + if (!$customCacheExpiration || (is_int($customCacheExpiration) && $customCacheExpiration > 0)) { + $data = $this->cache->load($query); + if ($data !== null) { + return $data; + } + } + } + + public function _saveToCache($query, $data, $customCacheExpiration) + { + if (!$customCacheExpiration) { + $this->cache->save($query, $data); + } elseif (is_int($customCacheExpiration) && $customCacheExpiration > 0) { + $this->cache->save($query, $data, $customCacheExpiration); + } + } + + public function loggerSqlFormat($table, $fields) + { + + } } diff --git a/src/Lib/Database/DatabaseAdapter.php b/src/Lib/Database/DatabaseAdapter.php index 8ff35b6b..e765a310 100644 --- a/src/Lib/Database/DatabaseAdapter.php +++ b/src/Lib/Database/DatabaseAdapter.php @@ -10,88 +10,88 @@ */ class DatabaseAdapter { - protected $dbDriver; - protected $selfDescription; // ? - - public function __construct(&$dbDescription) - { - $this->init($dbDescription); - } - - /** - * @param \NLDBDescription $dbDescription - */ - public function init($dbDescription) - { - $this->dbDriver = $dbDescription->getDriver(); - $this->selfDescription = $dbDescription; - } - - public function switchDatabase($dbDescription) - { - $this->close(); - $this->init($dbDescription); - } - - public function __get($property) - { - switch ($property) { - case 'db': - case 'connection': - FatalError('You cant use deprecated $' . $property . ' property'); - break; - - case 'cache': - return $this->dbDriver->cache; - } - } - - public function __call($method, $args) - { - if (!method_exists($this->dbDriver, $method)) { - FatalError('Undefined method ' . $method . ' in ' . get_class($this->dbDriver)); - } - - return call_user_func_array([$this->dbDriver, $method], $args); - } - - public function query($query, $donotusecache = false, $limitpair = null) - { - return $this->dbDriver->query($query, $donotusecache, $limitpair); - } - - public function TrueStatusString($status = 'TRUE') - { - return ($status == 'TRUE' || $status == 1) ? "'1'" : "'0'"; - } - - public function ClearCache($force = false) - { - return $this->dbDriver->cache->clear(); - } - - public function setCache($on) - { - return $on ? $this->cacheOn() : $this->cacheOff(); - } - - public function cacheOn() - { - $this->dbDriver->setCache($this->selfDescription->cache); - } - - public function cacheOff() - { - $this->dbDriver->setCache(false); - } - - public function getSelfDescription() - { - return $this->selfDescription; - } - - public function LIKE($condition, $percs = null) - { - return $this->dbDriver->LIKE($condition, is_null($percs) ? P_LEFT | P_RIGHT : $percs); - } + protected $dbDriver; + protected $selfDescription; // ? + + public function __construct(&$dbDescription) + { + $this->init($dbDescription); + } + + /** + * @param \NLDBDescription $dbDescription + */ + public function init($dbDescription) + { + $this->dbDriver = $dbDescription->getDriver(); + $this->selfDescription = $dbDescription; + } + + public function switchDatabase($dbDescription) + { + $this->close(); + $this->init($dbDescription); + } + + public function __get($property) + { + switch ($property) { + case 'db': + case 'connection': + FatalError('You cant use deprecated $' . $property . ' property'); + break; + + case 'cache': + return $this->dbDriver->cache; + } + } + + public function __call($method, $args) + { + if (!method_exists($this->dbDriver, $method)) { + FatalError('Undefined method ' . $method . ' in ' . $this->dbDriver::class); + } + + return call_user_func_array([$this->dbDriver, $method], $args); + } + + public function query($query, $donotusecache = false, $limitpair = null) + { + return $this->dbDriver->query($query, $donotusecache, $limitpair); + } + + public function TrueStatusString($status = 'TRUE') + { + return ($status == 'TRUE' || $status == 1) ? "'1'" : "'0'"; + } + + public function ClearCache($force = false) + { + return $this->dbDriver->cache->clear(); + } + + public function setCache($on) + { + return $on ? $this->cacheOn() : $this->cacheOff(); + } + + public function cacheOn() + { + $this->dbDriver->setCache($this->selfDescription->cache); + } + + public function cacheOff() + { + $this->dbDriver->setCache(false); + } + + public function getSelfDescription() + { + return $this->selfDescription; + } + + public function LIKE($condition, $percs = null) + { + return $this->dbDriver->LIKE($condition, is_null($percs) ? P_LEFT | P_RIGHT : $percs); + } } diff --git a/src/Lib/Database/DatabaseException.php b/src/Lib/Database/DatabaseException.php index ed5f6021..4b9fde45 100644 --- a/src/Lib/Database/DatabaseException.php +++ b/src/Lib/Database/DatabaseException.php @@ -5,5 +5,6 @@ /** * Class DatabaseException */ -class DatabaseException extends \Exception { +class DatabaseException extends \Exception +{ } diff --git a/src/Lib/Database/DatabaseInterface.php b/src/Lib/Database/DatabaseInterface.php index 2520a07d..20160d2b 100644 --- a/src/Lib/Database/DatabaseInterface.php +++ b/src/Lib/Database/DatabaseInterface.php @@ -2,9 +2,9 @@ namespace PP\Lib\Database; -interface DatabaseInterface { - - public function setUser($user); - public function loadDirectoriesAutomatic(&$directories); +interface DatabaseInterface +{ + public function setUser($user); + public function loadDirectoriesAutomatic(&$directories); } diff --git a/src/Lib/Database/Driver/PostgreSqlDriver.php b/src/Lib/Database/Driver/PostgreSqlDriver.php index be724ff2..25987a50 100644 --- a/src/Lib/Database/Driver/PostgreSqlDriver.php +++ b/src/Lib/Database/Driver/PostgreSqlDriver.php @@ -11,439 +11,442 @@ */ class PostgreSqlDriver extends AbstractSqlDatabase { - - public const TYPE = 'pgsql'; - - public $connectString; - public $connection; - - /** @var CacheInterface */ - public $cache; - - /** @var bool */ - public $connected; - - protected $includeOptions = ['connect_timeout' => '']; - protected $excludeOptions = ['encoding']; - - protected $insideTransaction = false; - protected $transactionsStack = []; - - /** - * @param \NLDBDescription $dbDescription - */ - public function __construct($dbDescription) - { - $this->setCache($dbDescription->cache); - $this->connectArray = array_merge($this->connectArray, $this->includeOptions); - $this->setConnectParams($dbDescription); - $this->connected = false; - } - - public function setConnectParams($dbDescription) - { - $keys = array_keys($this->connectArray); - $connectString = []; - - foreach ($keys as $key) { - if ($dbDescription->$key !== null) { - $this->connectArray[$key] = $dbDescription->$key; // TODO: refactor - if (!in_array($key, $this->excludeOptions)) { - $connectString[] = $key . '=' . $dbDescription->$key; // TODO: refactor - } - } - } - - $this->connectString = trim(implode(' ', $connectString)); - } - - public function Connect() - { - if (!$this->connected) { - $this->connection = pg_connect($this->connectString, PGSQL_CONNECT_FORCE_NEW) or FatalError('Can\'t connect to ' . $this->connectString); - - if ($this->connection) { - pg_exec($this->connection, "SET DATESTYLE='German'"); - - if (!empty($this->connectArray['encoding'])) { - pg_set_client_encoding($this->connection, $this->connectArray['encoding']); - } - - $this->connected = true; - } - } - return $this->connected; - } - - public function Close() - { - if ($this->connected) { - pg_close($this->connection); - $this->connected = false; - } - } - - public function ModifyingQuery($query, $table = null, $retField = null, $flushCache = true, $retCount = false) - { - if (!$this->connected) { - $this->Connect(); - } - - if ($this->connected) { - if ($retField) { - $retField = $this->EscapeString($retField); - $query .= " RETURNING {$retField}"; - } - - if (($res = pg_query($this->connection, $query)) == false) { - return ERROR_DB_BADQUERY; - } - - if ($flushCache == true) { - $this->cache->clear(); // TODO: refactor - } - - if ($table && $retField) { - if ($returnResult = pg_fetch_result($res, 0, $retField)) { - return $returnResult; - } else { - return null; - } - } elseif ($retCount) { - return pg_affected_rows($res); - } else { - return $res; - } - - } else { - return ERROR_DB_CANNOTCONNECT; - } - } - - public function ModifyingCopy($tableName, $cols, $data) - { - if (!$this->connected) { - $this->Connect(); - } - - $row = reset($data); - if (empty($row)) { - return ERROR_DB_BADQUERY; - } - - if ($this->connected) { - - $query = "COPY {$tableName} (\"" . implode('", "', $cols) . "\") FROM stdin"; - - if (pg_query($this->connection, $query) == false) { - return ERROR_DB_BADQUERY; - } - - $from = ["\\", "\r", "\n", "\t", "\1"]; - $to = ["\\\\", "\\r", "\\n", "\\t", "\t"]; - $lines = []; - - do { - $line = str_replace($from, $to, implode("\1", $row)); - - while (false !== strpos($line, "\t\t")) { - $line = str_replace("\t\t", "\t\\N\t", $line); - } - - if ($line[0] == "\t") { - $line = "\\N" . $line; - } - - if ($line[strlen($line) - 1] == "\t") { - $line .= "\\N"; - } - - $lines[] = $line; - - } while ($row = next($data)); - - if (pg_put_line($this->connection, join("\n", $lines) . "\n") == false) { - return ERROR_DB_BADQUERY; - } - - if (pg_put_line($this->connection, "\\.\n") == false) { - return ERROR_DB_BADQUERY; - } - - if (pg_end_copy($this->connection) == false) { - return ERROR_DB_BADQUERY; - } - - } else { - return ERROR_DB_CANNOTCONNECT; - } - - return true; - } - - public function Query($query, $donotusecache = false, $limitpair = null) - { - if (is_array($limitpair)) { - $limitstring = " LIMIT {$limitpair[0]} OFFSET {$limitpair[1]}"; - } else { - $limitstring = ""; - } - - $query .= $limitstring; - - if (!is_null($table = $this->_loadFromCache($query, $donotusecache))) { - return $table; - } - - if (!$this->connected) { - $this->Connect(); - } - - if ($this->connected) { - if (($res = pg_query($this->connection, $query)) === false) { - return ERROR_DB_BADQUERY; - } - - $table = []; - $total = pg_num_rows($res); - - for ($i = 0; $i < $total; $i++) { - $table[] = pg_fetch_assoc($res, $i); - } - - $this->_saveToCache($query, $table, $donotusecache); - } else { - return ERROR_DB_CANNOTCONNECT; - } - - return $table; - } - - public function InsertObject($table, $fields, $values, $flushCache = true) - { - $query = "INSERT INTO {$table} (\"" . implode('", "', $fields) . "\") VALUES (" . implode(', ', array_map([$this, '__mapInsertData'], $values)) . ")"; - return $this->ModifyingQuery($query, $table, 'id', $flushCache); - } - - public function MapData($value) - { - switch (true) { - case is_null($value) || $value === '' : - return "NULL"; - case $value === "##now##" || $value === "now()": - return "now()"; - case is_bool($value): - return $value ? "'t'" : "'f'"; - default: - return "'" . $this->EscapeString($value) . "'"; - } - } - - public function mapFields($field) - { - return '"' . $this->EscapeString($field) . '"'; - } - - public function __mapInsertData($value) - { - return $this->MapData($value); - } - - public function __mapUpdateData($field, $value) - { - return "\"{$field}\" = " . $this->MapData($value); - } - - public function EscapeString($s) - { - return pg_escape_string($s); - } - - public function UpdateObjectById($table, $objectId, $fields, $values, $flushCache = true) - { - $query = "UPDATE {$table} SET " . implode(', ', array_map([$this, '__mapUpdateData'], $fields, $values)) . " WHERE id={$objectId}"; - return $this->ModifyingQuery($query, null, null, $flushCache); - } - - - /** - * @return $this - */ - public function transactionBegin() - { - if (!$this->insideTransaction) { - $this->Query('BEGIN', true); - $this->insideTransaction = true; - } else { // emulating nested transactions - $savepointId = uniqid('px_'); - $this->savepointCreate($savepointId); - $this->transactionsStack[] = $savepointId; - } - return $this; - } - - /** - * @return $this - */ - public function transactionCommit() - { - if (empty($this->transactionsStack)) { - $this->Query('END', true); - $this->insideTransaction = false; - } else { // emulating nested transactions - $savepointId = array_pop($this->transactionsStack); - $this->savepointRelease($savepointId); - } - return $this; - } - - public function transactionRollback() - { - if (empty($this->transactionsStack)) { - $this->Query('ROLLBACK', true); - $this->insideTransaction = false; - } else { // emulating nested transactions - $savepointId = array_pop($this->transactionsStack); - $this->savepointRollbackTo($savepointId); - } - } - - /** - * Named transactions (savepoints) related functions - */ - public function savepointCreate($id) - { - $this->Query('SAVEPOINT ' . $id, true); - } - - public function savepointRelease($id) - { - $this->Query('RELEASE SAVEPOINT ' . $id, true); - } - - public function savepointRollbackTo($id) - { - $this->Query('ROLLBACK TO ' . $id, true); - } - - - public function importDateTime($string) - { - return $string; - } - - public function exportFloat($string) - { - return str_replace(',', '.', $string); - } - - public function exportDateTime($string) - { - return $string == '00.00. 00:00:00' ? null : $string; - } - - public function importBoolean($string) - { - return $string == 't' || $string == '1'; - } - - public function IsUniqueColsCombination($tables, $colValues, $exception) - { - if (!is_array($tables) || !sizeof($tables)) { - FatalError("Вы не указали проверяемые таблицы"); - } - - if (!is_array($colValues) || !sizeof($colValues)) { - FatalError("Вы не указали проверяемые столбцы"); - } - - $query = "SELECT (0 "; - foreach ($tables as $t) { - [$where_clauses, $tableName] = [[], $t['tableName']]; - - foreach ($colValues as $c => $v) { - $where_clauses[] = sprintf("%s = '%s'", $c, $v); - } - - if (sizeof($exception)) { - foreach ($exception as $c => $v) { - $where_clauses[] = sprintf("%s != '%s'", $c, $v); - } - } - - $query = sprintf("%s + (SELECT count(*) FROM %s WHERE %s", - $query, $tableName, join(" AND ", $where_clauses)); - - if ($t['exWhere']) { - $query = sprintf("%s and %s", $query, $t['exWhere']); - } - - $query .= ")"; - } - $query .= ")"; - - return (int)(current(current($this->Query($query, true)))); // TODO: refactor - } - - public function getError() - { - return pg_last_error(); - } - - public function tableExists($tableName) - { - return count($this->query("SELECT relname FROM pg_class WHERE relname='{$tableName}'")); - } - - public function LIKE($condition, $percs) - { - return $this->_searchMethod("ILIKE", $condition, $percs); - } - - public function inArray($arrayField, $value, $sane = false) - { - return sprintf("%s @> ARRAY[%s]", $sane ? $arrayField : $this->EscapeString($arrayField), $sane ? $value : $this->EscapeString($value)); - } - - public function arrayIn($arrayField, $value, $sane = false) - { - return sprintf("%s <@ ARRAY[%s]", $sane ? $arrayField : $this->EscapeString($arrayField), $sane ? $value : $this->EscapeString($value)); - } - - public function intersectIntArray($arrayField, $values) - { - return sprintf("%s && '{%s}'", - $this->EscapeString($arrayField), - join(",", array_filter($values, "is_numeric"))); - } - - public function ifStatement($when, $then, $else = null) - { - $else = $else ? "ELSE ({$else})" : ''; - return sprintf("CASE WHEN (%s) THEN (%s) %s END", $when, $then, $else); - } - - public function caseStatement($arr, $else = null) - { - $else = $else ? "ELSE ({$else})" : ''; - $whenthens = ''; - foreach ((array)$arr as $when => $then) { - $whenthens .= sprintf('WHEN (%s) THEN (%s) ', $when, $then); - } - return sprintf("CASE %s %s END", $whenthens, $else); - } - - public function vacuumTable($tableName) - { - $this->Query("VACUUM " . $tableName, true); - $this->Query("VACUUM ANALYZE " . $tableName, true); - } - - public function loggerSqlFormat($table, $fields) - { - if (!count($fields)) return false; - $fieldNames = implode(', ', array_map([&$this, "mapFields"], array_keys($fields))); - $fieldValues = implode(', ', array_map([&$this, "MapData"], array_values($fields))); - return sprintf("INSERT INTO %s (%s) VALUES(%s)", $table, $fieldNames, $fieldValues); - } + public const TYPE = 'pgsql'; + + public $connectString; + public $connection; + + /** @var CacheInterface */ + public $cache; + + /** @var bool */ + public $connected; + + protected $includeOptions = ['connect_timeout' => '']; + protected $excludeOptions = ['encoding']; + + protected $insideTransaction = false; + protected $transactionsStack = []; + + /** + * @param \NLDBDescription $dbDescription + */ + public function __construct($dbDescription) + { + $this->setCache($dbDescription->cache); + $this->connectArray = array_merge($this->connectArray, $this->includeOptions); + $this->setConnectParams($dbDescription); + $this->connected = false; + } + + public function setConnectParams($dbDescription) + { + $keys = array_keys($this->connectArray); + $connectString = []; + + foreach ($keys as $key) { + if ($dbDescription->$key !== null) { + $this->connectArray[$key] = $dbDescription->$key; // TODO: refactor + if (!in_array($key, $this->excludeOptions)) { + $connectString[] = $key . '=' . $dbDescription->$key; // TODO: refactor + } + } + } + + $this->connectString = trim(implode(' ', $connectString)); + } + + public function Connect() + { + if (!$this->connected) { + $this->connection = pg_connect($this->connectString, PGSQL_CONNECT_FORCE_NEW) or FatalError('Can\'t connect to ' . $this->connectString); + + if ($this->connection) { + pg_exec($this->connection, "SET DATESTYLE='German'"); + + if (!empty($this->connectArray['encoding'])) { + pg_set_client_encoding($this->connection, $this->connectArray['encoding']); + } + + $this->connected = true; + } + } + return $this->connected; + } + + public function Close() + { + if ($this->connected) { + pg_close($this->connection); + $this->connected = false; + } + } + + public function ModifyingQuery($query, $table = null, $retField = null, $flushCache = true, $retCount = false) + { + if (!$this->connected) { + $this->Connect(); + } + + if ($this->connected) { + if ($retField) { + $retField = $this->EscapeString($retField); + $query .= " RETURNING {$retField}"; + } + + if (($res = pg_query($this->connection, $query)) == false) { + return ERROR_DB_BADQUERY; + } + + if ($flushCache == true) { + $this->cache->clear(); // TODO: refactor + } + + if ($table && $retField) { + if ($returnResult = pg_fetch_result($res, 0, $retField)) { + return $returnResult; + } else { + return null; + } + } elseif ($retCount) { + return pg_affected_rows($res); + } else { + return $res; + } + + } else { + return ERROR_DB_CANNOTCONNECT; + } + } + + public function ModifyingCopy($tableName, $cols, $data) + { + if (!$this->connected) { + $this->Connect(); + } + + $row = reset($data); + if (empty($row)) { + return ERROR_DB_BADQUERY; + } + + if ($this->connected) { + + $query = "COPY {$tableName} (\"" . implode('", "', $cols) . "\") FROM stdin"; + + if (pg_query($this->connection, $query) == false) { + return ERROR_DB_BADQUERY; + } + + $from = ["\\", "\r", "\n", "\t", "\1"]; + $to = ["\\\\", "\\r", "\\n", "\\t", "\t"]; + $lines = []; + + do { + $line = str_replace($from, $to, implode("\1", $row)); + + while (str_contains($line, "\t\t")) { + $line = str_replace("\t\t", "\t\\N\t", $line); + } + + if ($line[0] == "\t") { + $line = "\\N" . $line; + } + + if ($line[strlen($line) - 1] == "\t") { + $line .= "\\N"; + } + + $lines[] = $line; + + } while ($row = next($data)); + + if (pg_put_line($this->connection, join("\n", $lines) . "\n") == false) { + return ERROR_DB_BADQUERY; + } + + if (pg_put_line($this->connection, "\\.\n") == false) { + return ERROR_DB_BADQUERY; + } + + if (pg_end_copy($this->connection) == false) { + return ERROR_DB_BADQUERY; + } + + } else { + return ERROR_DB_CANNOTCONNECT; + } + + return true; + } + + public function Query($query, $donotusecache = false, $limitpair = null) + { + if (is_array($limitpair)) { + $limitstring = " LIMIT {$limitpair[0]} OFFSET {$limitpair[1]}"; + } else { + $limitstring = ""; + } + + $query .= $limitstring; + + if (!is_null($table = $this->_loadFromCache($query, $donotusecache))) { + return $table; + } + + if (!$this->connected) { + $this->Connect(); + } + + if ($this->connected) { + if (($res = pg_query($this->connection, $query)) === false) { + return ERROR_DB_BADQUERY; + } + + $table = []; + $total = pg_num_rows($res); + + for ($i = 0; $i < $total; $i++) { + $table[] = pg_fetch_assoc($res, $i); + } + + $this->_saveToCache($query, $table, $donotusecache); + } else { + return ERROR_DB_CANNOTCONNECT; + } + + return $table; + } + + public function InsertObject($table, $fields, $values, $flushCache = true) + { + $query = "INSERT INTO {$table} (\"" . implode('", "', $fields) . "\") VALUES (" . implode(', ', array_map($this->__mapInsertData(...), $values)) . ")"; + return $this->ModifyingQuery($query, $table, 'id', $flushCache); + } + + public function MapData($value) + { + return match (true) { + is_null($value) || $value === '' => "NULL", + $value === "##now##" || $value === "now()" => "now()", + is_bool($value) => $value ? "'t'" : "'f'", + default => "'" . $this->EscapeString($value) . "'", + }; + } + + public function mapFields($field) + { + return '"' . $this->EscapeString($field) . '"'; + } + + public function __mapInsertData($value) + { + return $this->MapData($value); + } + + public function __mapUpdateData($field, $value) + { + return "\"{$field}\" = " . $this->MapData($value); + } + + public function EscapeString($s) + { + return pg_escape_string($s); + } + + public function UpdateObjectById($table, $objectId, $fields, $values, $flushCache = true) + { + $query = "UPDATE {$table} SET " . implode(', ', array_map($this->__mapUpdateData(...), $fields, $values)) . " WHERE id={$objectId}"; + return $this->ModifyingQuery($query, null, null, $flushCache); + } + + + /** + * @return $this + */ + public function transactionBegin() + { + if (!$this->insideTransaction) { + $this->Query('BEGIN', true); + $this->insideTransaction = true; + } else { // emulating nested transactions + $savepointId = uniqid('px_'); + $this->savepointCreate($savepointId); + $this->transactionsStack[] = $savepointId; + } + return $this; + } + + /** + * @return $this + */ + public function transactionCommit() + { + if (empty($this->transactionsStack)) { + $this->Query('END', true); + $this->insideTransaction = false; + } else { // emulating nested transactions + $savepointId = array_pop($this->transactionsStack); + $this->savepointRelease($savepointId); + } + return $this; + } + + public function transactionRollback() + { + if (empty($this->transactionsStack)) { + $this->Query('ROLLBACK', true); + $this->insideTransaction = false; + } else { // emulating nested transactions + $savepointId = array_pop($this->transactionsStack); + $this->savepointRollbackTo($savepointId); + } + } + + /** + * Named transactions (savepoints) related functions + */ + public function savepointCreate($id) + { + $this->Query('SAVEPOINT ' . $id, true); + } + + public function savepointRelease($id) + { + $this->Query('RELEASE SAVEPOINT ' . $id, true); + } + + public function savepointRollbackTo($id) + { + $this->Query('ROLLBACK TO ' . $id, true); + } + + + public function importDateTime($string) + { + return $string; + } + + public function exportFloat($string) + { + return str_replace(',', '.', (string) $string); + } + + public function exportDateTime($string) + { + return $string == '00.00. 00:00:00' ? null : $string; + } + + public function importBoolean($string) + { + return $string == 't' || $string == '1'; + } + + public function IsUniqueColsCombination($tables, $colValues, $exception) + { + if (!is_array($tables) || !sizeof($tables)) { + FatalError("Вы не указали проверяемые таблицы"); + } + + if (!is_array($colValues) || !sizeof($colValues)) { + FatalError("Вы не указали проверяемые столбцы"); + } + + $query = "SELECT (0 "; + foreach ($tables as $t) { + [$where_clauses, $tableName] = [[], $t['tableName']]; + + foreach ($colValues as $c => $v) { + $where_clauses[] = sprintf("%s = '%s'", $c, $v); + } + + if (sizeof($exception)) { + foreach ($exception as $c => $v) { + $where_clauses[] = sprintf("%s != '%s'", $c, $v); + } + } + + $query = sprintf( + "%s + (SELECT count(*) FROM %s WHERE %s", + $query, + $tableName, + join(" AND ", $where_clauses) + ); + + if ($t['exWhere']) { + $query = sprintf("%s and %s", $query, $t['exWhere']); + } + + $query .= ")"; + } + $query .= ")"; + + return (int)(current(current($this->Query($query, true)))); // TODO: refactor + } + + public function getError() + { + return pg_last_error(); + } + + public function tableExists($tableName) + { + return is_countable($this->query("SELECT relname FROM pg_class WHERE relname='{$tableName}'")) ? count($this->query("SELECT relname FROM pg_class WHERE relname='{$tableName}'")) : 0; + } + + public function LIKE($condition, $percs) + { + return $this->_searchMethod("ILIKE", $condition, $percs); + } + + public function inArray($arrayField, $value, $sane = false) + { + return sprintf("%s @> ARRAY[%s]", $sane ? $arrayField : $this->EscapeString($arrayField), $sane ? $value : $this->EscapeString($value)); + } + + public function arrayIn($arrayField, $value, $sane = false) + { + return sprintf("%s <@ ARRAY[%s]", $sane ? $arrayField : $this->EscapeString($arrayField), $sane ? $value : $this->EscapeString($value)); + } + + public function intersectIntArray($arrayField, $values) + { + return sprintf( + "%s && '{%s}'", + $this->EscapeString($arrayField), + join(",", array_filter($values, "is_numeric")) + ); + } + + public function ifStatement($when, $then, $else = null) + { + $else = $else ? "ELSE ({$else})" : ''; + return sprintf("CASE WHEN (%s) THEN (%s) %s END", $when, $then, $else); + } + + public function caseStatement($arr, $else = null) + { + $else = $else ? "ELSE ({$else})" : ''; + $whenthens = ''; + foreach ((array)$arr as $when => $then) { + $whenthens .= sprintf('WHEN (%s) THEN (%s) ', $when, $then); + } + return sprintf("CASE %s %s END", $whenthens, $else); + } + + public function vacuumTable($tableName) + { + $this->Query("VACUUM " . $tableName, true); + $this->Query("VACUUM ANALYZE " . $tableName, true); + } + + public function loggerSqlFormat($table, $fields) + { + if (!(is_countable($fields) ? count($fields) : 0)) { + return false; + } + $fieldNames = implode(', ', array_map($this->mapFields(...), array_keys($fields))); + $fieldValues = implode(', ', array_map($this->MapData(...), array_values($fields))); + return sprintf("INSERT INTO %s (%s) VALUES(%s)", $table, $fieldNames, $fieldValues); + } } diff --git a/src/Lib/Datastruct/Leaf.php b/src/Lib/Datastruct/Leaf.php index 76fa0d4a..bf74a89b 100644 --- a/src/Lib/Datastruct/Leaf.php +++ b/src/Lib/Datastruct/Leaf.php @@ -6,86 +6,86 @@ * Class Leaf * @package PP\Lib\Datastruct */ -class Leaf { - public $id; - public $title; - public $children; - public $parent; - public $content; +class Leaf +{ + public $children; - /** - * @var int - */ - public $level; + /** + * @var int + */ + public $level; - /** - * @var Tree - */ - public $tree; + /** + * @var Tree + */ + public $tree; - /** - * Leaf constructor. - * - * @param $id - * @param $title - * @param $parentId - * @param $content - * @param Tree $tree - */ - public function __construct($id, $title, $parentId, $content, Tree $tree) { - $this->id = $id; - $this->title = $title; - $this->children = []; - $this->parent = $parentId; - $this->content = $content; - $this->level = 0; - $this->tree = $tree; - } + /** + * Leaf constructor. + * + * @param $id + * @param $title + * @param $parentId + * @param $content + */ + public function __construct(public $id, public $title, public $parent, public $content, Tree $tree) + { + $this->children = []; + $this->level = 0; + $this->tree = $tree; + } - public function getAncestors($andSelf = false) { - return $this->tree->getAncestors($this->id, $andSelf); - } + public function getAncestors($andSelf = false) + { + return $this->tree->getAncestors($this->id, $andSelf); + } - public function parent($level = 1) { - $ancestors = $this->getAncestors(); + public function parent($level = 1) + { + $ancestors = $this->getAncestors(); - if ($level < 0) { - $level = -$level + 1; - $ancestors = array_reverse($ancestors); - } + if ($level < 0) { + $level = -$level + 1; + $ancestors = array_reverse($ancestors); + } - if (isset($ancestors[$level - 1])) { - return $ancestors[$level - 1]; - } else { - return null; - } - } + if (isset($ancestors[$level - 1])) { + return $ancestors[$level - 1]; + } else { + return null; + } + } - public function getDescendants($level = null) { - return $this->tree->getDescendants($this->id, $level); - } + public function getDescendants($level = null) + { + return $this->tree->getDescendants($this->id, $level); + } - public function createpath() { - return createPathByParentId($this->tree, $this->id); - } + public function createpath() + { + return createPathByParentId($this->tree, $this->id); + } - public function createpathWithoutRoot() { - return createSomePathByParentId($this->tree, $this->id, 'pathname', '/', true, false); - } + public function createpathWithoutRoot() + { + return createSomePathByParentId($this->tree, $this->id, 'pathname', '/', true, false); + } - /** - * Determines whether the current leaf is root - * - * @return bool - */ - public function isRoot() { - return $this->id == $this->tree->getRoot()->id; - } + /** + * Determines whether the current leaf is root + * + * @return bool + */ + public function isRoot() + { + return $this->id == $this->tree->getRoot()->id; + } - /** - * Removes itself from the tree - */ - public function tearOff() { - unset($this->tree->leafs[$this->id]); - } + /** + * Removes itself from the tree + */ + public function tearOff() + { + unset($this->tree->leafs[$this->id]); + } } diff --git a/src/Lib/Datastruct/Tree.php b/src/Lib/Datastruct/Tree.php index adedd02a..7c1394e3 100644 --- a/src/Lib/Datastruct/Tree.php +++ b/src/Lib/Datastruct/Tree.php @@ -9,376 +9,386 @@ * @todo add chaining support * @todo access level for "leafs" and "levels" should be protected */ -class Tree { - /** @var Leaf[] */ - public $leafs; - - /** @var array */ - public $levels; - - // TODO: Database access to this vars - public $_idField; - - public $_parentField; - - public $_titleField; - - /** - * Tree constructor. - * - * @param array $table - * @param string $idField - * @param string $parentField - * @param string $titleField - * @param bool $saveOrphans - */ - public function __construct( - $table, - $idField = 'id', - $parentField = 'parent', - $titleField = 'title', - $saveOrphans = false - ) { - $this->_idField = $idField; - $this->_parentField = $parentField; - $this->_titleField = $titleField; - - $this->leafs = []; - $this->levels = []; - $this->leafs[0] = new Leaf(0, 'Root', null, [], $this); - - $this->saveOrphans = $saveOrphans; - - $this->fillLeafs($table); - $this->fillLevel(0, 0); - } - - /** - * @param bool $saveOrphans - */ - public function setSaveOrphans($saveOrphans) { - $this->saveOrphans = !!$saveOrphans; - } - - /** - * Recursively collects all objects of allowed types within - * children - * - * @param int $structId - * @param array $allowedTypes - * @return array - */ - public function recursiveChildren($structId, array $allowedTypes = []) { - $childrenIds = $this->leafs[$structId]->children; - if (empty($childrenIds)) { - return []; - } - - $result = [$structId]; - - foreach ($childrenIds as $childId) { - $item = $this->leafs[$childId]->content; - if (!empty($allowedTypes) && !in_array($item['type'], $allowedTypes)) { - continue; - } - - $result[] = $childId; - $tmp = $this->recursiveChildren($childId, $allowedTypes); - if (!empty($tmp)) { - $result = array_merge($result, $tmp); - } - } - - return $result; - } - - protected function fillLeafs($table) { - // Filling leafs of tree - foreach ($table as $k => $v) { - $id = $v[$this->_idField]; - $title = $v[$this->_titleField]; - - // For creating trees from plain - $parent = (isset($v[$this->_parentField])) ? $v[$this->_parentField] : 0; - $this->leafs[$id] = new Leaf($id, $title, $parent, $v, $this); - } - - // Filling children attributes of leafs of tree - $toUnset = []; - - foreach ($this->leafs as $k => $v) { - if (!is_null($v->parent) && isset($this->leafs[$v->parent])) { - $this->leafs[$v->parent]->children[] = $k; - - } elseif ($v->id != 0 && $this->saveOrphans) { - $this->leafs[0]->children[] = $k; - } elseif ($v->id != 0 && !$this->saveOrphans) { - $toUnset[] = $k; - } - } - - foreach ($toUnset as $key) { - unset($this->leafs[$key]); - } - } - - protected function fillLevel($leafId, $level) { - // Filling level array - $this->levels[$level][] = $leafId; - $this->leafs[$leafId]->level = $level; - - foreach ($this->leafs[$leafId]->children as $childId) { - $this->fillLevel($childId, $level + 1); - } - } - - public function walk(callable $closure, $rootId = null) { - $rootId = ($rootId) ?: 0; - - if (empty($this->leafs[$rootId])) { - throw new \Exception("Absent rootId: {$rootId}"); - } - - $leafs = $this->leafs; - $traversing = function ($id) use (&$leafs, $closure, &$traversing) { - $leaf = $leafs[$id]; - $closure($leaf); - - if (!empty($leaf->children)) { - foreach ($leaf->children as $id => $child) { - $traversing($child); - } - } - }; - - return $traversing($rootId); - } - - public function map(callable $closure, $rootId = null) { - $rootId = ($rootId) ?: 0; - - if (empty($this->leafs[$rootId])) { - throw new \Exception("Absent rootId: {$rootId}"); - } - - $leafs = $this->leafs; - $traversing = function ($id) use (&$leafs, $closure, &$traversing) { - $leaf = $leafs[$id]; - $result = []; - - if (!empty($leaf->children)) { - foreach ($leaf->children as $id => $child) { - $result[] = $traversing($child); - } - } - - return $closure($leaf, $result); - }; - - $result = []; - foreach ($leafs[$rootId]->children as $id => $child) { - $result[] = $traversing($child); - } - - return $result; - } - - /** - * Gets tree's root leaf - * - * @return Leaf - */ - public function getRoot() { - return $this->leafs[0]; - } - - public function getFullPath($id) { - if (!isset($this->leafs[$id])) { - return []; - } - - $ret = [$id]; - while ($id != 0) { - if (!isset($this->leafs[$id])) { - return []; - } - $id = $this->leafs[$id]->parent; - if ($id != 0) $ret[] = $id; - } - return array_reverse($ret); - } - - public function getFullPathString($id, $varName = 'pathname', $omitFirst = true) { - $pathArray = $this->getFullPath($id); - $pathString = null; - if (is_array($pathArray) && count($pathArray) && isset($this->leafs[$pathArray[0]]->content[$varName])) { - $pathString = '/'; - foreach ($pathArray as $k => $v) { - if ($omitFirst && $k == 0) { - continue; - } - $pathString .= $this->leafs[$v]->content[$varName] . '/'; - } - } - return $pathString; - } - - public function getIdArrayByPath($varName, $pathArray) { - $idArray = []; - $id = 0; - - while (count($pathArray)) { - $tmpFlag = 0; - - foreach ($this->leafs[$id]->children as $leafId) { - if ($this->leafs[$leafId]->content[$varName] == $pathArray[0]) { - $idArray[] = $leafId; - $id = $leafId; - $tmpFlag = 1; - } - } - - if (!$tmpFlag) { - $idArray[] = -1; - return $idArray; // CHECK ME - } - - array_shift($pathArray); - } - - return $idArray; - } - - public function getPlainTree( - $restrictedId, - $id = 0, - $parent = null, - $current = null, - $level = 1, - $prefix = '' - ) { - $t = []; - - foreach ($this->leafs[$id]->children as $child) { - if ($child == $restrictedId) { - continue; - } - - if ($child != $current) { - $t[$child] = $prefix . ' ' . $this->leafs[$child]->title; - $t = $t + $this->getPlainTree($restrictedId, $child, $parent, $current, $level + 1, $prefix . '==='); - } - } - - if ($id == 0) { - $t[null] = 'Корень'; // TODO: lang - } - - return $t; - } - - public function isAncestor($id, $testId) { - if (isset($this->leafs[$id]->parent) && $this->leafs[$id]->parent) { - if ($this->leafs[$id]->parent == $testId) { - return true; - } else { - return $this->isAncestor($this->leafs[$id]->parent, $testId); - } - - } else { - return false; - } - } - - public function getDescendantsOrSelf($parents) { - $retArray = []; - $parents = array_flip($parents); - - foreach ($this->leafs as $leaf) { - if (isset($parents[$leaf->id])) { - $retArray = array_merge($retArray, $this->getDescendants($leaf->id)); - } - } - - return $retArray; - } - - public function getDescendants($id, $level = null) { - $retArray[] = $id; - - if (isset($this->leafs[$id]->children) && ($level === null || $this->leafs[$id]->level < $level)) { - foreach ($this->leafs[$id]->children as $child) { - $retArray = array_merge($retArray, $this->getDescendants($child, $level)); - } - } - - return $retArray; - } - - public function getAncestors($id, $andSelf = false) { - $ancestors = []; - - if (isset($this->leafs[$id]) && $this->leafs[$id]->parent !== null) { - if ($andSelf) { - $ancestors = [$id, $this->leafs[$id]->parent]; - } else { - $ancestors = [$this->leafs[$id]->parent]; - } - - $ancestors = array_merge($ancestors, $this->getAncestors($this->leafs[$id]->parent)); - } - - return $ancestors; - } - - public function addLeaf(Leaf $leaf) { - $parentId = $leaf->parent; - $parentId = (is_null($parentId)) ? 0 : $parentId; - $parentExists = isset($this->leafs[$parentId]); - - if ($parentExists || $this->saveOrphans) { - $this->leafs[$leaf->id] = $leaf; - - if ($parentExists) { - $this->leafs[$parentId]->children[] = $leaf->id; - } - } - } - - /** - * Removes leaf by id from the tree - * - * @param int $leafId - */ - public function removeLeaf($leafId) { - if ($leafId == 0) { - return; - } - - if (isset($this->leafs[$this->leafs[$leafId]->parent])) { - if (($l = array_search($leafId, $this->leafs[$this->leafs[$leafId]->parent]->children)) !== false) { - unset($this->leafs[$this->leafs[$leafId]->parent]->children[$l]); - } - $this->leafs[$this->leafs[$leafId]->parent]->children = array_values($this->leafs[$this->leafs[$leafId]->parent]->children); - } - - unset($this->leafs[$leafId]); - } - - public function toTable() { - $table = []; - foreach ($this->leafs as $l) { - if ($l->id == 0) { - continue; - } - - $object = $l->content; - $object[$this->_idField] = $l->id; - $object[$this->_parentField] = $l->parent; - $object[$this->_titleField] = $l->title; - $table[$l->id] = $object; - } - - return $table; - } +class Tree +{ + /** @var Leaf[] */ + public $leafs; + + /** @var array */ + public $levels; + + /** + * Tree constructor. + * + * @param array $table + * @param string $_idField + * @param string $_parentField + * @param string $_titleField + * @param bool $saveOrphans + */ + public function __construct( + $table, + // TODO: Database access to this vars + public $_idField = 'id', + public $_parentField = 'parent', + public $_titleField = 'title', + $saveOrphans = false + ) { + $this->leafs = []; + $this->levels = []; + $this->leafs[0] = new Leaf(0, 'Root', null, [], $this); + + $this->saveOrphans = $saveOrphans; + + $this->fillLeafs($table); + $this->fillLevel(0, 0); + } + + /** + * @param bool $saveOrphans + */ + public function setSaveOrphans($saveOrphans) + { + $this->saveOrphans = !!$saveOrphans; + } + + /** + * Recursively collects all objects of allowed types within + * children + * + * @param int $structId + * @return array + */ + public function recursiveChildren($structId, array $allowedTypes = []) + { + $childrenIds = $this->leafs[$structId]->children; + if (empty($childrenIds)) { + return []; + } + + $result = [$structId]; + + foreach ($childrenIds as $childId) { + $item = $this->leafs[$childId]->content; + if (!empty($allowedTypes) && !in_array($item['type'], $allowedTypes)) { + continue; + } + + $result[] = $childId; + $tmp = $this->recursiveChildren($childId, $allowedTypes); + if (!empty($tmp)) { + $result = array_merge($result, $tmp); + } + } + + return $result; + } + + protected function fillLeafs($table) + { + // Filling leafs of tree + foreach ($table as $k => $v) { + $id = $v[$this->_idField]; + $title = $v[$this->_titleField]; + + // For creating trees from plain + $parent = $v[$this->_parentField] ?? 0; + $this->leafs[$id] = new Leaf($id, $title, $parent, $v, $this); + } + + // Filling children attributes of leafs of tree + $toUnset = []; + + foreach ($this->leafs as $k => $v) { + if (!is_null($v->parent) && isset($this->leafs[$v->parent])) { + $this->leafs[$v->parent]->children[] = $k; + + } elseif ($v->id != 0 && $this->saveOrphans) { + $this->leafs[0]->children[] = $k; + } elseif ($v->id != 0 && !$this->saveOrphans) { + $toUnset[] = $k; + } + } + + foreach ($toUnset as $key) { + unset($this->leafs[$key]); + } + } + + protected function fillLevel($leafId, $level) + { + // Filling level array + $this->levels[$level][] = $leafId; + $this->leafs[$leafId]->level = $level; + + foreach ($this->leafs[$leafId]->children as $childId) { + $this->fillLevel($childId, $level + 1); + } + } + + public function walk(callable $closure, $rootId = null) + { + $rootId = ($rootId) ?: 0; + + if (empty($this->leafs[$rootId])) { + throw new \Exception("Absent rootId: {$rootId}"); + } + + $leafs = $this->leafs; + $traversing = function ($id) use (&$leafs, $closure, &$traversing) { + $leaf = $leafs[$id]; + $closure($leaf); + + if (!empty($leaf->children)) { + foreach ($leaf->children as $id => $child) { + $traversing($child); + } + } + }; + + return $traversing($rootId); + } + + public function map(callable $closure, $rootId = null) + { + $rootId = ($rootId) ?: 0; + + if (empty($this->leafs[$rootId])) { + throw new \Exception("Absent rootId: {$rootId}"); + } + + $leafs = $this->leafs; + $traversing = function ($id) use (&$leafs, $closure, &$traversing) { + $leaf = $leafs[$id]; + $result = []; + + if (!empty($leaf->children)) { + foreach ($leaf->children as $id => $child) { + $result[] = $traversing($child); + } + } + + return $closure($leaf, $result); + }; + + $result = []; + foreach ($leafs[$rootId]->children as $id => $child) { + $result[] = $traversing($child); + } + + return $result; + } + + /** + * Gets tree's root leaf + * + * @return Leaf + */ + public function getRoot() + { + return $this->leafs[0]; + } + + public function getFullPath($id) + { + if (!isset($this->leafs[$id])) { + return []; + } + + $ret = [$id]; + while ($id != 0) { + if (!isset($this->leafs[$id])) { + return []; + } + $id = $this->leafs[$id]->parent; + if ($id != 0) { + $ret[] = $id; + } + } + return array_reverse($ret); + } + + public function getFullPathString($id, $varName = 'pathname', $omitFirst = true) + { + $pathArray = $this->getFullPath($id); + $pathString = null; + if (is_array($pathArray) && count($pathArray) && isset($this->leafs[$pathArray[0]]->content[$varName])) { + $pathString = '/'; + foreach ($pathArray as $k => $v) { + if ($omitFirst && $k == 0) { + continue; + } + $pathString .= $this->leafs[$v]->content[$varName] . '/'; + } + } + return $pathString; + } + + public function getIdArrayByPath($varName, $pathArray) + { + $idArray = []; + $id = 0; + + while (is_countable($pathArray) ? count($pathArray) : 0) { + $tmpFlag = 0; + + foreach ($this->leafs[$id]->children as $leafId) { + if ($this->leafs[$leafId]->content[$varName] == $pathArray[0]) { + $idArray[] = $leafId; + $id = $leafId; + $tmpFlag = 1; + } + } + + if (!$tmpFlag) { + $idArray[] = -1; + return $idArray; // CHECK ME + } + + array_shift($pathArray); + } + + return $idArray; + } + + public function getPlainTree( + $restrictedId, + $id = 0, + $parent = null, + $current = null, + $level = 1, + $prefix = '' + ) { + $t = []; + + foreach ($this->leafs[$id]->children as $child) { + if ($child == $restrictedId) { + continue; + } + + if ($child != $current) { + $t[$child] = $prefix . ' ' . $this->leafs[$child]->title; + $t = $t + $this->getPlainTree($restrictedId, $child, $parent, $current, $level + 1, $prefix . '==='); + } + } + + if ($id == 0) { + $t[null] = 'Корень'; // TODO: lang + } + + return $t; + } + + public function isAncestor($id, $testId) + { + if (isset($this->leafs[$id]->parent) && $this->leafs[$id]->parent) { + if ($this->leafs[$id]->parent == $testId) { + return true; + } else { + return $this->isAncestor($this->leafs[$id]->parent, $testId); + } + + } else { + return false; + } + } + + public function getDescendantsOrSelf($parents) + { + $retArray = []; + $parents = array_flip($parents); + + foreach ($this->leafs as $leaf) { + if (isset($parents[$leaf->id])) { + $retArray = array_merge($retArray, $this->getDescendants($leaf->id)); + } + } + + return $retArray; + } + + public function getDescendants($id, $level = null) + { + $retArray = []; + $retArray[] = $id; + + if (isset($this->leafs[$id]->children) && ($level === null || $this->leafs[$id]->level < $level)) { + foreach ($this->leafs[$id]->children as $child) { + $retArray = array_merge($retArray, $this->getDescendants($child, $level)); + } + } + + return $retArray; + } + + public function getAncestors($id, $andSelf = false) + { + $ancestors = []; + + if (isset($this->leafs[$id]) && $this->leafs[$id]->parent !== null) { + if ($andSelf) { + $ancestors = [$id, $this->leafs[$id]->parent]; + } else { + $ancestors = [$this->leafs[$id]->parent]; + } + + $ancestors = array_merge($ancestors, $this->getAncestors($this->leafs[$id]->parent)); + } + + return $ancestors; + } + + public function addLeaf(Leaf $leaf) + { + $parentId = $leaf->parent; + $parentId = (is_null($parentId)) ? 0 : $parentId; + $parentExists = isset($this->leafs[$parentId]); + + if ($parentExists || $this->saveOrphans) { + $this->leafs[$leaf->id] = $leaf; + + if ($parentExists) { + $this->leafs[$parentId]->children[] = $leaf->id; + } + } + } + + /** + * Removes leaf by id from the tree + * + * @param int $leafId + */ + public function removeLeaf($leafId) + { + if ($leafId == 0) { + return; + } + + if (isset($this->leafs[$this->leafs[$leafId]->parent])) { + if (($l = array_search($leafId, $this->leafs[$this->leafs[$leafId]->parent]->children)) !== false) { + unset($this->leafs[$this->leafs[$leafId]->parent]->children[$l]); + } + $this->leafs[$this->leafs[$leafId]->parent]->children = array_values($this->leafs[$this->leafs[$leafId]->parent]->children); + } + + unset($this->leafs[$leafId]); + } + + public function toTable() + { + $table = []; + foreach ($this->leafs as $l) { + if ($l->id == 0) { + continue; + } + + $object = $l->content; + $object[$this->_idField] = $l->id; + $object[$this->_parentField] = $l->parent; + $object[$this->_titleField] = $l->title; + $table[$l->id] = $object; + } + + return $table; + } } diff --git a/src/Lib/Engine/AbstractEngine.php b/src/Lib/Engine/AbstractEngine.php index bdc587a8..0cf7e6d7 100644 --- a/src/Lib/Engine/AbstractEngine.php +++ b/src/Lib/Engine/AbstractEngine.php @@ -15,193 +15,192 @@ abstract class AbstractEngine implements EngineInterface { - - /** @var \PXModuleDescription[] */ - protected $modules; - - /** @var string */ - protected $area; - - /** @var ApplicationFactory */ - protected $app = ['factory' => 'PP\ApplicationFactory', 'helper' => true]; - - /** @var \PXRequest */ - protected $request = ['factory' => 'PXRequest']; - - /** @var \PXDatabase|PostgreSqlDriver */ - protected $db = ['factory' => 'PXDatabase', 'helper' => true]; - - /** @var LayoutInterface */ - protected $layout = ['factory' => 'PP\Lib\Html\Layout\NullLayout']; - - /** @var \PXUserAuthorized */ - protected $user = ['factory' => 'PXUserAuthorized']; - - /** - * @var \Symfony\Component\DependencyInjection\ContainerInterface - */ - protected $container = ['factory' => 'Symfony\Component\DependencyInjection\ContainerBuilder', 'helper' => true]; - - /** - * @var ConfigCache - */ - protected $containerConfigCache; - - /** - * @var array - */ - protected $initOrder = ['container', 'app', 'db', 'request', 'user', 'layout']; - - // TODO: refactor - public function __construct() - { - \PXRegistry::setEngine($this); - - $this->initApplication(); - $this->saveToRegistry(); - $this->compileContainer(); - } - - /** - * {@inheritdoc} - */ - public function start() - { - $this->user->setDb($this->db); - $this->user->setApp($this->app); - $this->user->setRequest($this->request); - $this->user->checkAuth(); - - /** @var \PXTriggerDescription $t */ - foreach ($this->app->triggers->system as $t) { - - /** @var \PXAbstractSystemTrigger $trigger */ - $trigger = $t->getTrigger(); - $trigger->onAfterEngineStart($this); - } - - $this->app->loadProperties($this->db); - $this->db->loadDirectoriesAutomatic($this->app->directory); - - $this->initModules(); - - return $this; - } - - /** - * Initializes DI container. - * - * @param string $klass - * @throws \InvalidArgumentException if services.yml is not found - * @throws \Exception - */ - protected function initContainer($klass) - { - $file = CACHE_PATH . DIRECTORY_SEPARATOR . 'container.php'; - $this->containerConfigCache = new ConfigCache($file, false); - - if ($this->containerConfigCache->isFresh()) { - require_once $file; - $this->container = new \MyCachedContainer(); - return; - } - - $path = APPPATH . 'config'; - /** @var ContainerBuilder $container */ - $container = new $klass(); - $container->registerExtension(new CoreExtension()); - $container - ->addCompilerPass(new AddLoggingHandlersPass()) - ->addCompilerPass(new RegisterListenersPass()); - - $loader = new YamlFileLoader($container, new FileLocator($path)); - - $loader->load('services.yml'); - - $this->container = $container; - } - - protected function compileContainer() - { - if (!$this->containerConfigCache->isFresh()) { - $this->container->compile(true); - } - } - - protected function initApplication() - { - foreach ($this->initOrder as $var) { - if (!is_array($this->$var) || empty($this->{$var}['factory'])) { - continue; - } - - if (!class_exists($this->{$var}['factory'])) { - \FatalError("Factory class '{$this->{$var}['factory']}' for '{$var}' is not exists !"); - } - - $initHelper = (!empty($this->{$var}['helper']) && method_exists($this, 'init' . ucfirst($var)) ? 'init' . ucfirst($var) : false); - - if ($initHelper) { - $this->$initHelper($this->{$var}['factory']); - } else { - $this->$var = new $this->{$var}['factory'](); - } - } - } - - protected function saveToRegistry() - { - foreach (array_keys(get_object_vars($this)) as $var) { - if (is_object($this->$var) && \PXRegistry::canSaveIt($var)) { - call_user_func_array(['PXRegistry', 'set' . ucfirst($var)], [&$this->$var]); - } - } - } - - protected function initApp($klass) - { - $this->app = ApplicationFactory::create($this); - } - - protected function initDb($klass) - { - $this->db = new $klass($this->app); - } - - protected function initModules() - { - $rArea = $this->request->getArea(); - - if (!isset($this->app->modules[$rArea])) { - Finalize('/'); - } - - $this->modules = $this->app->modules; - $this->area = $rArea; - } - - public abstract function runModules(); - - public function engineClass() - { - return static::USER_ENGINE_ID; - } - - protected function checkArea($area) - { - if (!isset($this->modules[$area])) { - FatalError('Некорректный параметр area = ' . strip_tags($this->area) . ''); - } - } - - public function getContainer() - { - return $this->container; - } - - /** {@inheritdoc} */ - abstract public function engineType(); - - /** {@inheritdoc} */ - abstract public function engineBehavior(); + /** @var \PXModuleDescription[] */ + protected $modules; + + /** @var string */ + protected $area; + + /** @var ApplicationFactory */ + protected $app = ['factory' => \PP\ApplicationFactory::class, 'helper' => true]; + + /** @var \PXRequest */ + protected $request = ['factory' => 'PXRequest']; + + /** @var \PXDatabase|PostgreSqlDriver */ + protected $db = ['factory' => 'PXDatabase', 'helper' => true]; + + /** @var LayoutInterface */ + protected $layout = ['factory' => \PP\Lib\Html\Layout\NullLayout::class]; + + /** @var \PXUserAuthorized */ + protected $user = ['factory' => 'PXUserAuthorized']; + + /** + * @var \Symfony\Component\DependencyInjection\ContainerInterface + */ + protected $container = ['factory' => \Symfony\Component\DependencyInjection\ContainerBuilder::class, 'helper' => true]; + + /** + * @var ConfigCache + */ + protected $containerConfigCache; + + /** + * @var array + */ + protected $initOrder = ['container', 'app', 'db', 'request', 'user', 'layout']; + + // TODO: refactor + public function __construct() + { + \PXRegistry::setEngine($this); + + $this->initApplication(); + $this->saveToRegistry(); + $this->compileContainer(); + } + + /** + * {@inheritdoc} + */ + public function start() + { + $this->user->setDb($this->db); + $this->user->setApp($this->app); + $this->user->setRequest($this->request); + $this->user->checkAuth(); + + /** @var \PXTriggerDescription $t */ + foreach ($this->app->triggers->system as $t) { + + /** @var \PXAbstractSystemTrigger $trigger */ + $trigger = $t->getTrigger(); + $trigger->onAfterEngineStart($this); + } + + $this->app->loadProperties($this->db); + $this->db->loadDirectoriesAutomatic($this->app->directory); + + $this->initModules(); + + return $this; + } + + /** + * Initializes DI container. + * + * @param string $klass + * @throws \InvalidArgumentException if services.yml is not found + * @throws \Exception + */ + protected function initContainer($klass) + { + $file = CACHE_PATH . DIRECTORY_SEPARATOR . 'container.php'; + $this->containerConfigCache = new ConfigCache($file, false); + + if ($this->containerConfigCache->isFresh()) { + require_once $file; + $this->container = new \MyCachedContainer(); + return; + } + + $path = APPPATH . 'config'; + /** @var ContainerBuilder $container */ + $container = new $klass(); + $container->registerExtension(new CoreExtension()); + $container + ->addCompilerPass(new AddLoggingHandlersPass()) + ->addCompilerPass(new RegisterListenersPass()); + + $loader = new YamlFileLoader($container, new FileLocator($path)); + + $loader->load('services.yml'); + + $this->container = $container; + } + + protected function compileContainer() + { + if (!$this->containerConfigCache->isFresh()) { + $this->container->compile(true); + } + } + + protected function initApplication() + { + foreach ($this->initOrder as $var) { + if (!is_array($this->$var) || empty($this->{$var}['factory'])) { + continue; + } + + if (!class_exists($this->{$var}['factory'])) { + \FatalError("Factory class '{$this->{$var}['factory']}' for '{$var}' is not exists !"); + } + + $initHelper = (!empty($this->{$var}['helper']) && method_exists($this, 'init' . ucfirst((string) $var)) ? 'init' . ucfirst((string) $var) : false); + + if ($initHelper) { + $this->$initHelper($this->{$var}['factory']); + } else { + $this->$var = new $this->{$var}['factory'](); + } + } + } + + protected function saveToRegistry() + { + foreach (array_keys(get_object_vars($this)) as $var) { + if (is_object($this->$var) && \PXRegistry::canSaveIt($var)) { + call_user_func_array(['PXRegistry', 'set' . ucfirst($var)], [&$this->$var]); + } + } + } + + protected function initApp($klass) + { + $this->app = ApplicationFactory::create($this); + } + + protected function initDb($klass) + { + $this->db = new $klass($this->app); + } + + protected function initModules() + { + $rArea = $this->request->getArea(); + + if (!isset($this->app->modules[$rArea])) { + Finalize('/'); + } + + $this->modules = $this->app->modules; + $this->area = $rArea; + } + + abstract public function runModules(); + + public function engineClass() + { + return static::USER_ENGINE_ID; + } + + protected function checkArea($area) + { + if (!isset($this->modules[$area])) { + FatalError('Некорректный параметр area = ' . strip_tags($this->area) . ''); + } + } + + public function getContainer() + { + return $this->container; + } + + /** {@inheritdoc} */ + abstract public function engineType(); + + /** {@inheritdoc} */ + abstract public function engineBehavior(); } diff --git a/src/Lib/Engine/Admin/AbstractAdminEngine.php b/src/Lib/Engine/Admin/AbstractAdminEngine.php index 9dd784aa..fa4e5f66 100644 --- a/src/Lib/Engine/Admin/AbstractAdminEngine.php +++ b/src/Lib/Engine/Admin/AbstractAdminEngine.php @@ -8,49 +8,54 @@ use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Session; -abstract class AbstractAdminEngine extends AbstractEngine { - - public const SESSION_NAME = 'sid'; - - // TODO: auth module should export authorizable behaviour, like this -> (bool)$module->thisIsAdminAuthModule() - protected $authArea = 'auth'; - - /** - * @var Session - */ - protected $session = ['factory' => 'Symfony\Component\HttpFoundation\Session\Session', 'helper' => true]; - - protected $initOrder = ['container', 'app', 'db', 'request', 'session', 'user', 'layout']; - - public function engineClass() { - return static::ADMIN_ENGINE_ID; - } - - protected function initSession($klass) { - $storage = new NativeSessionStorage([ - 'cookie_httponly' => true, - 'cookie_secure' => \PXRegistry::getRequest()->GetHttpProto() === 'https', - 'use_strict_mode' => true, - ], new DatabaseHandler($this->db)); - - $this->session = new $klass($storage); - $this->session->setName(static::SESSION_NAME); - $this->session->start(); - } - - // static because PXEngineAdminJSON inherited from PXEngineJSON (stupid, but traits available only from php 5.4) - protected function getModule(PXApplication $app, $area) { - return array_filter([$area => $app->getAvailableModule($area)]); - } - - protected function hasAdminModules() { - // $user->isAdmin() is obsolete for Admin Engines, - // because $this->modules are filtered with $user->can('admin'...) before - return count($this->modules) > 1 || (count($this->modules) == 1 && !isset($this->modules[$this->authArea])); - } - /** {@inheritdoc} */ - public function engineType() { - return static::ADMIN_ENGINE_TAG; - } +abstract class AbstractAdminEngine extends AbstractEngine +{ + public const SESSION_NAME = 'sid'; + + // TODO: auth module should export authorizable behaviour, like this -> (bool)$module->thisIsAdminAuthModule() + protected $authArea = 'auth'; + + /** + * @var Session + */ + protected $session = ['factory' => \Symfony\Component\HttpFoundation\Session\Session::class, 'helper' => true]; + + protected $initOrder = ['container', 'app', 'db', 'request', 'session', 'user', 'layout']; + + public function engineClass() + { + return static::ADMIN_ENGINE_ID; + } + + protected function initSession($klass) + { + $storage = new NativeSessionStorage([ + 'cookie_httponly' => true, + 'cookie_secure' => \PXRegistry::getRequest()->GetHttpProto() === 'https', + 'use_strict_mode' => true, + ], new DatabaseHandler($this->db)); + + $this->session = new $klass($storage); + $this->session->setName(static::SESSION_NAME); + $this->session->start(); + } + + // static because PXEngineAdminJSON inherited from PXEngineJSON (stupid, but traits available only from php 5.4) + protected function getModule(PXApplication $app, $area) + { + return array_filter([$area => $app->getAvailableModule($area)]); + } + + protected function hasAdminModules() + { + // $user->isAdmin() is obsolete for Admin Engines, + // because $this->modules are filtered with $user->can('admin'...) before + return count($this->modules) > 1 || (count($this->modules) == 1 && !isset($this->modules[$this->authArea])); + } + /** {@inheritdoc} */ + public function engineType() + { + return static::ADMIN_ENGINE_TAG; + } } diff --git a/src/Lib/Engine/Admin/AdminEngineIndex.php b/src/Lib/Engine/Admin/AdminEngineIndex.php index 38b6b4c1..920bc8be 100644 --- a/src/Lib/Engine/Admin/AdminEngineIndex.php +++ b/src/Lib/Engine/Admin/AdminEngineIndex.php @@ -14,146 +14,145 @@ */ class AdminEngineIndex extends AbstractAdminEngine { - - /** @var AdminHtmlLayout */ - protected $layout = ['factory' => 'PP\Lib\Html\Layout\AdminHtmlLayout', 'helper' => true]; - protected $menu; - protected $outerLayout = 'index'; - protected $templateMainArea = 'INNER.0.0'; - - public function initLayout($klass) - { - $this->layout = new $klass($this->outerLayout, $this->app->types); - } - - public function initModules() - { - $this->modules = $this->app->getAvailableModules(); - } - - public function initMenu() - { - $menuItems = []; - - foreach ($this->modules as $module) { - // check modules acl rules - if ($this->user->can('viewmenu', $module)) { - if ($module->getDescription() == '' || $module->getDescription() == PXModuleDescription::EMPTY_DESCRIPTION) { - $menuItems[$module->getName()] = $module->getName(); - } else { - $menuItems[$module->getName()] = $module->getDescription(); - } - } - } - - $this->menu = $menuItems; - } - - public function showAuthForm() - { - if (!isset($this->modules[$this->authArea])) { - FatalError('Undefined auth module or you forget insert "allo" for "admin" auth module in acl_objects'); - } - $moduleDescription = $this->modules[$this->authArea]; - $auth = $moduleDescription->getModule(); - - $eventData = [ - 'engine_type' => $this->engineType(), - 'engine_behavior' => $this->engineBehavior(), - ]; - foreach ($this->app->triggers->system as $t) { - $t->getTrigger()->onBeforeModuleRun($this, $moduleDescription, $eventData); - } - - $auth->adminIndex(); - - foreach ($this->app->triggers->system as $t) { - $t->getTrigger()->onAfterModuleRun($this, $moduleDescription, $eventData); - } - } - - public function fillLayout() - { - $this->layout->assignFlashes(); - $this->layout->setLogoutForm('?area=exit'); - $this->layout->setMenu($this->menu, $this->area, 'area', false); - - $this->layout->setTwoColumns(); - - $this->layout->setGetVarToSave('area', $this->area); - $this->layout->setGetVarToSave('sid', $this->request->getSid()); - } - - protected function checkArea($area) - { - if (!isset($this->modules[$area])) { - $this->layout->setOneColumn(); - $this->layout->assignError($this->templateMainArea, 'Некорректный параметр area = ' . strip_tags($area) . ''); - $this->layout->assignTitle('Некорректный параметр area'); - return false; - } - - return true; - } - - public function runModules() - { - $this->initMenu(); - - if (!$this->hasAdminModules()) { - $this->showAuthForm(); - return; - } - - $this->area = $this->request->getArea(current(array_keys($this->menu))); - $this->fillLayout(); - - if ($this->area == 'exit') { - $this->session->invalidate(1); - - $response = Response::getInstance(); - $response->redirect(sprintf('action.phtml?area=%s&action=exit', $this->authArea)); - } - - if (!$this->checkArea($this->area)) { - return; - } - - $moduleDescription = $this->modules[$this->area]; - $instance = $moduleDescription->getModule(); - if ($instance instanceof ContainerAwareInterface) { - $instance->setContainer($this->container); - } - - $eventData = [ - 'engine_type' => $this->engineType(), - 'engine_behavior' => $this->engineBehavior(), - ]; - foreach ($this->app->triggers->system as $t) { - $t->getTrigger()->onBeforeModuleRun($this, $moduleDescription, $eventData); - } - - $instance->adminIndex(); - - foreach ($this->app->triggers->system as $t) { - $t->getTrigger()->onAfterModuleRun($this, $moduleDescription, $eventData); - } - } - - public function html() - { - $response = Response::getInstance(); - $response->dontCache(); - - $charset = $this->app->getProperty('OUTPUT_CHARSET', DEFAULT_CHARSET); - - $this->db->Close(); - $this->layout->flush($charset); - } - - /** {@inheritdoc} */ - public function engineBehavior() - { - return static::INDEX_BEHAVIOR; - } + /** @var AdminHtmlLayout */ + protected $layout = ['factory' => \PP\Lib\Html\Layout\AdminHtmlLayout::class, 'helper' => true]; + protected $menu; + protected $outerLayout = 'index'; + protected $templateMainArea = 'INNER.0.0'; + + public function initLayout($klass) + { + $this->layout = new $klass($this->outerLayout, $this->app->types); + } + + public function initModules() + { + $this->modules = $this->app->getAvailableModules(); + } + + public function initMenu() + { + $menuItems = []; + + foreach ($this->modules as $module) { + // check modules acl rules + if ($this->user->can('viewmenu', $module)) { + if ($module->getDescription() == '' || $module->getDescription() == PXModuleDescription::EMPTY_DESCRIPTION) { + $menuItems[$module->getName()] = $module->getName(); + } else { + $menuItems[$module->getName()] = $module->getDescription(); + } + } + } + + $this->menu = $menuItems; + } + + public function showAuthForm() + { + if (!isset($this->modules[$this->authArea])) { + FatalError('Undefined auth module or you forget insert "allo" for "admin" auth module in acl_objects'); + } + $moduleDescription = $this->modules[$this->authArea]; + $auth = $moduleDescription->getModule(); + + $eventData = [ + 'engine_type' => $this->engineType(), + 'engine_behavior' => $this->engineBehavior(), + ]; + foreach ($this->app->triggers->system as $t) { + $t->getTrigger()->onBeforeModuleRun($this, $moduleDescription, $eventData); + } + + $auth->adminIndex(); + + foreach ($this->app->triggers->system as $t) { + $t->getTrigger()->onAfterModuleRun($this, $moduleDescription, $eventData); + } + } + + public function fillLayout() + { + $this->layout->assignFlashes(); + $this->layout->setLogoutForm('?area=exit'); + $this->layout->setMenu($this->menu, $this->area, 'area', false); + + $this->layout->setTwoColumns(); + + $this->layout->setGetVarToSave('area', $this->area); + $this->layout->setGetVarToSave('sid', $this->request->getSid()); + } + + protected function checkArea($area) + { + if (!isset($this->modules[$area])) { + $this->layout->setOneColumn(); + $this->layout->assignError($this->templateMainArea, 'Некорректный параметр area = ' . strip_tags((string) $area) . ''); + $this->layout->assignTitle('Некорректный параметр area'); + return false; + } + + return true; + } + + public function runModules() + { + $this->initMenu(); + + if (!$this->hasAdminModules()) { + $this->showAuthForm(); + return; + } + + $this->area = $this->request->getArea(current(array_keys($this->menu))); + $this->fillLayout(); + + if ($this->area == 'exit') { + $this->session->invalidate(1); + + $response = Response::getInstance(); + $response->redirect(sprintf('action.phtml?area=%s&action=exit', $this->authArea)); + } + + if (!$this->checkArea($this->area)) { + return; + } + + $moduleDescription = $this->modules[$this->area]; + $instance = $moduleDescription->getModule(); + if ($instance instanceof ContainerAwareInterface) { + $instance->setContainer($this->container); + } + + $eventData = [ + 'engine_type' => $this->engineType(), + 'engine_behavior' => $this->engineBehavior(), + ]; + foreach ($this->app->triggers->system as $t) { + $t->getTrigger()->onBeforeModuleRun($this, $moduleDescription, $eventData); + } + + $instance->adminIndex(); + + foreach ($this->app->triggers->system as $t) { + $t->getTrigger()->onAfterModuleRun($this, $moduleDescription, $eventData); + } + } + + public function html() + { + $response = Response::getInstance(); + $response->dontCache(); + + $charset = $this->app->getProperty('OUTPUT_CHARSET', DEFAULT_CHARSET); + + $this->db->Close(); + $this->layout->flush($charset); + } + + /** {@inheritdoc} */ + public function engineBehavior() + { + return static::INDEX_BEHAVIOR; + } } diff --git a/src/Lib/Engine/Admin/AdminEngineInterface.php b/src/Lib/Engine/Admin/AdminEngineInterface.php index 525b2c5c..7fd235aa 100644 --- a/src/Lib/Engine/Admin/AdminEngineInterface.php +++ b/src/Lib/Engine/Admin/AdminEngineInterface.php @@ -2,6 +2,6 @@ namespace PP\Lib\Engine\Admin; -interface AdminEngineInterface { - +interface AdminEngineInterface +{ } diff --git a/src/Lib/Engine/Admin/AdminEngineJson.php b/src/Lib/Engine/Admin/AdminEngineJson.php index 772a230d..7c6e4f18 100644 --- a/src/Lib/Engine/Admin/AdminEngineJson.php +++ b/src/Lib/Engine/Admin/AdminEngineJson.php @@ -12,55 +12,54 @@ */ class AdminEngineJson extends AbstractAdminEngine { + protected $result; - protected $result; + public function initModules() + { + $this->area = $this->request->getArea(); + $this->modules = $this->getModule($this->app, $this->area); + } - public function initModules() - { - $this->area = $this->request->getArea(); - $this->modules = $this->getModule($this->app, $this->area); - } + public function runModules() + { + // For correct user session expiration handling and admin auth module working + if (!($this->hasAdminModules() || $this->area == $this->authArea)) { + return; + } - public function runModules() - { - // For correct user session expiration handling and admin auth module working - if (!($this->hasAdminModules() || $this->area == $this->authArea)) { - return; - } + $this->checkArea($this->area); - $this->checkArea($this->area); + $moduleDescription = $this->modules[$this->area]; + $instance = $moduleDescription->getModule(); + if ($instance instanceof ContainerAwareInterface) { + $instance->setContainer($this->container); + } - $moduleDescription = $this->modules[$this->area]; - $instance = $moduleDescription->getModule(); - if ($instance instanceof ContainerAwareInterface) { - $instance->setContainer($this->container); - } + $eventData = [ + 'engine_type' => $this->engineType(), + 'engine_behavior' => $this->engineBehavior(), + ]; + foreach ($this->app->triggers->system as $t) { + $t->getTrigger()->onBeforeModuleRun($this, $moduleDescription, $eventData); + } - $eventData = [ - 'engine_type' => $this->engineType(), - 'engine_behavior' => $this->engineBehavior(), - ]; - foreach ($this->app->triggers->system as $t) { - $t->getTrigger()->onBeforeModuleRun($this, $moduleDescription, $eventData); - } + $this->result = $instance->adminJson(); - $this->result = $instance->adminJson(); + foreach ($this->app->triggers->system as $t) { + $t->getTrigger()->onAfterModuleRun($this, $moduleDescription, $eventData); + } + } - foreach ($this->app->triggers->system as $t) { - $t->getTrigger()->onAfterModuleRun($this, $moduleDescription, $eventData); - } - } + public function sendJson(): void + { + $response = Response::getInstance(); + $response->sendJson($this->result); + exit; + } - public function sendJson() - { - $response = Response::getInstance(); - $response->sendJson($this->result); - exit; - } - - /** {@inheritdoc} */ - public function engineBehavior() - { - return static::JSON_BEHAVIOR; - } + /** {@inheritdoc} */ + public function engineBehavior() + { + return static::JSON_BEHAVIOR; + } } diff --git a/src/Lib/Engine/Admin/AdminEnginePopup.php b/src/Lib/Engine/Admin/AdminEnginePopup.php index eb3877b4..91952809 100644 --- a/src/Lib/Engine/Admin/AdminEnginePopup.php +++ b/src/Lib/Engine/Admin/AdminEnginePopup.php @@ -11,7 +11,6 @@ */ class AdminEnginePopup extends AdminEngineIndex { - protected $outerLayout = 'popup'; protected $templateMainArea = 'OUTER.CONTENT'; diff --git a/src/Lib/Engine/EngineInterface.php b/src/Lib/Engine/EngineInterface.php index e444a71d..480f031f 100644 --- a/src/Lib/Engine/EngineInterface.php +++ b/src/Lib/Engine/EngineInterface.php @@ -5,72 +5,72 @@ /** * Engine interface */ -interface EngineInterface { +interface EngineInterface +{ + /** @var int User engine id */ + public const USER_ENGINE_ID = 1; - /** @var int User engine id */ - public const USER_ENGINE_ID = 1; + /** @var int Admin engine id */ + public const ADMIN_ENGINE_ID = 2; - /** @var int Admin engine id */ - public const ADMIN_ENGINE_ID = 2; + /** @var int Sbin engine id */ + public const SBIN_ENGINE_ID = 3; - /** @var int Sbin engine id */ - public const SBIN_ENGINE_ID = 3; + /** @var string User engine tag */ + public const USER_ENGINE_TAG = 'user'; - /** @var string User engine tag */ - public const USER_ENGINE_TAG = 'user'; + /** @var string Admin engine tag */ + public const ADMIN_ENGINE_TAG = 'admin'; - /** @var string Admin engine tag */ - public const ADMIN_ENGINE_TAG = 'admin'; + /** @var string Sbin engine tag */ + public const SBIN_ENGINE_TAG = 'sbin'; - /** @var string Sbin engine tag */ - public const SBIN_ENGINE_TAG = 'sbin'; + /** @var string Index engine behavior tag */ + public const INDEX_BEHAVIOR = 'index'; + /** @var string Action engine behavior tag */ + public const ACTION_BEHAVIOR = 'action'; + /** @var string JSON engine behavior tag */ + public const JSON_BEHAVIOR = 'json'; + /** @var string Popup engine behavior tag */ + public const POPUP_BEHAVIOR = 'popup'; - /** @var string Index engine behavior tag */ - public const INDEX_BEHAVIOR = 'index'; - /** @var string Action engine behavior tag */ - public const ACTION_BEHAVIOR = 'action'; - /** @var string JSON engine behavior tag */ - public const JSON_BEHAVIOR = 'json'; - /** @var string Popup engine behavior tag */ - public const POPUP_BEHAVIOR = 'popup'; + /** + * Runs the Engine + * + * @return self + */ + public function start(); - /** - * Runs the Engine - * - * @return self - */ - public function start(); + /** + * Runs modules + */ + public function runModules(); - /** - * Runs modules - */ - public function runModules(); + /** + * Returns engine class ID + * + * @return int + */ + public function engineClass(); - /** - * Returns engine class ID - * - * @return int - */ - public function engineClass(); + /** + * Returns service container + * + * @return \Symfony\Component\DependencyInjection\Container + */ + public function getContainer(); - /** - * Returns service container - * - * @return \Symfony\Component\DependencyInjection\Container - */ - public function getContainer(); + /** + * Returns engine type tag + * + * @return string + */ + public function engineType(); - /** - * Returns engine type tag - * - * @return string - */ - public function engineType(); - - /** - * Returns engine behavior tag - * - * @return string - */ - public function engineBehavior(); + /** + * Returns engine behavior tag + * + * @return string + */ + public function engineBehavior(); } diff --git a/src/Lib/Html/Layout/AdminHtmlLayout.php b/src/Lib/Html/Layout/AdminHtmlLayout.php index 135ee09b..c84360bc 100644 --- a/src/Lib/Html/Layout/AdminHtmlLayout.php +++ b/src/Lib/Html/Layout/AdminHtmlLayout.php @@ -8,291 +8,290 @@ */ class AdminHtmlLayout extends LayoutAbstract { - - public $types; - public $_scripts = []; - - - //TODO: there must be right places in for this - public $_scriptsTemplates = [ - ':css' => [ - ':area' => 'OUTER.FORMBEGIN', - ':proto' => '', - ], - ':js' => [ - ':area' => 'OUTER.FORMEND', - ':proto' => '', - ], - ':inline_js' => [ - ':area' => 'OUTER.FORMEND', - ':proto' => "", - ], - ':inline_css' => [ - ':area' => 'OUTER.FORMBEGIN', - ':proto' => "", - ], - ]; - - public function __construct($outerLayout, $types) - { - parent::__construct(); - - $this->types = $types; - - $this->assignTitle("Proxima Portal"); - $this->setOuterLogo('i/admin.gif', 'Proxima Portal', 126, 36); - $this->setOuterLayout($outerLayout); - - $this->assign('BODY.CLASS', quot(\PXRegistry::getRequest()->getArea())); - } - - protected function assignVersion() - { - $user = \PXRegistry::getUser(); - $version = ($user->isAuthed()) - ? PP_VERSION - : ''; - - $this->assign('OUTER.VERSION', $version); - } - - /** - * Retrieves all flash messages from session, makes html and - * assigns it to layout. - */ - public function assignFlashes() - { - $htmlFlashes = ''; - $session = \PXRegistry::getSession(); - - foreach ($session->getFlashBag()->all() as $type => $messages) { - foreach ($messages as $message) { - $htmlFlashes .= sprintf( - '
×%s
', - $type, $message - ); - } - } - - $this->assign('FLASHES', $htmlFlashes); - } - - /** - * @param null|string $title - * @return $this - */ - public function assignTitle($title = null) - { - $title = ((is_null($title) || !mb_strlen(trim($title))) - ? '' - : mb_strtr($title, ['<' => '<', '>' => '>'])); - - $this->assign("OUTER.TITLE", $title); - - return $this; - } - - public function assignError($label, $errorText) - { - $this->assign($label, '

' . $errorText . '

'); - } - - public function assignContentControls($label, $selectedSid, $allowedFormats) - { - throw new \Exception("please use AdminHtmlLayout::AssignControls"); - } - - public function assignControls($label, $selectedSid, $allowedFormats) - { - $this->clear($label); - $this->appendControls($label, $selectedSid, $allowedFormats); - } - - public function appendControls($label, $selectedSid, $allowedFormats) - { - foreach ($allowedFormats as $format) { - $button = new \PXControlButton($this->types[$format]->title); - $button->setClickCode('AddContent(\'' . $format . '\', ' . (int)$selectedSid . ')'); - $button->setClass('add'); - - $button->addToParent($label); - } - } - - public function _makeContextMenu($selectedSid, $allowedFormats) - { - $html = ''; - - if (sizeof($allowedFormats)) { - $html .= ' onContextMenu="Context(event, \'add\', \'' . quot(quot($selectedSid), false) . '\''; - - foreach ($this->types as $k => $v) { - if (in_array($k, $allowedFormats)) { - $html .= ' , \'' . $k . '\', \'' . $v->title . '\''; - } - } - - $html .= '); return false;" '; - } - - return $html; - } - - public function assignContextMenu($label, $selectedSid, $allowedFormats) - { - $this->assign($label, $this->_makeContextMenu($selectedSid, $allowedFormats)); - } - - public function appendContextMenu($label, $selectedSid, $allowedFormats) - { - // store formats and labels for each parent - // some times there are same parents so we can share context menus - static $stored = []; - if (!isset($stored[$selectedSid])) { - $stored[$selectedSid] = [ - 'labels' => [], - 'formats' => [], - ]; - } - - // append to label - $collection = &$stored[$selectedSid]; - $collection['labels'][] = $label; - $collection['formats'] = array_merge($collection['formats'], $allowedFormats); - - // draw it for each label (each space) - foreach ($collection['labels'] as $_label) { - $this->assign($_label, $this->_makeContextMenu($selectedSid, $collection['formats'])); - } - } - - public function appendTable($label, $objectFormat, $table, $selected = null, $varToTitle = null, $page = 1, $objectsOnPage = 0, $count = 0, $withLinks = true, $parentPathname = null) - { - $htmltable = new \PXAdminTable($table, $this->types[$objectFormat], $this->getData); - $htmltable->setCaption($this->types[$objectFormat]->title . '(' . $count . ')'); - - $pager = new \PxAdminPager($page, $objectsOnPage, $count, $this->types[$objectFormat], $this->getData); - $htmltable->setPosition($pager->getPosition()); - - $this->append($label, $htmltable->html() . $pager->html()); - } - - public function appendUserTable($label, $objectFormat, $title, $table, &$userClassName, $userFuncName, $page = 1, $objectsOnPage = 0, $count = 0) - { - $this->Append($label, '

' . $title . ' (' . $count . ')

'); - $html = null; - $page = $page ? $page : 1; // ? - - if (!count($table)) { - $html = ''; - $this->Append($label, $html); - return; - } - - $html .= call_user_func([$userClassName, $userFuncName], 'header'); - foreach ($table as $rowPos => $row) { - $html .= call_user_func([$userClassName, $userFuncName], 'row', $row); - } - $html .= call_user_func([$userClassName, $userFuncName], 'footer'); - - if ($count > $objectsOnPage && $objectsOnPage > 0) { - $html .= '
'; - $html .= 'Страницы: '; - - $allPages = ceil($count / $objectsOnPage) + 1; - $start = (ceil($page / 10) - 1) * 10 + 1; - $max = $start + 10; - - if ($max > $allPages) { - $max = $allPages; - } - - if ($page > 10) { - $prev = (ceil($start / 10) - 1) * 10 - 9; - } - - $last = $allPages - $start - 10; - - if ($last > 0) { - $next = (ceil($start / 10) - 1) * 10 + 11; - } - - if (isset($prev)) { - $html .= ''; - $html .= 'Страница ' . $prev . ''; - $html .= ''; - } - - for ($i = $start; $i < $max; $i++) { - $html .= '' . $i . ' '; - } - - if (isset($next) && $next > 0) { - $html .= ''; - $html .= 'Страница ' . $next . ''; - $html .= ''; - } - - $html .= '
'; - } - $this->Append($label, $html); - } - - public function assignKeyValueList($label, $list, $selected, $varName = 'sid') - { - parent::AssignKeyValueList($label, $list, $varName, $selected); - } - - public function assignJS($pathToScript) - { - $this->_renderScript($pathToScript, ':js', true); - } - - public function assignInlineJS($scriptBody, $uniq = true) - { - $this->_renderScript($scriptBody, ':inline_js', $uniq); - } - - public function assignCSS($pathToScript) - { - $this->_renderScript($pathToScript, ':css', true); - } - - public function assignInlineCSS($scriptBody) - { - $this->_renderScript($scriptBody, ':inline_css', true); - } - - public function _renderScript($body, $template, $singleton) - { - $hash = md5($body); - if (!mb_strlen($body) || ($singleton && isset($this->_scripts[$hash]))) { - return false; - } - - $this->append($this->_scriptsTemplates[$template][':area'], sprintf($this->_scriptsTemplates[$template][':proto'], $body)); - - return $singleton ? ($this->_scripts[$hash] = true) : true; - } - - public function template($filename, $label = null) - { - $this->assignVersion(); - - return parent::template($filename, $label); - } - - public function notSetAllowedChilds($label, $format, $id) - { - $format = \PXRegistry::getTypes($format); - - $this->assign($label, << for this + public $_scriptsTemplates = [ + ':css' => [ + ':area' => 'OUTER.FORMBEGIN', + ':proto' => '', + ], + ':js' => [ + ':area' => 'OUTER.FORMEND', + ':proto' => '', + ], + ':inline_js' => [ + ':area' => 'OUTER.FORMEND', + ':proto' => "", + ], + ':inline_css' => [ + ':area' => 'OUTER.FORMBEGIN', + ':proto' => "", + ], + ]; + + public function __construct($outerLayout, public $types) + { + parent::__construct(); + + $this->assignTitle("Proxima Portal"); + $this->setOuterLogo('i/admin.gif', 'Proxima Portal', 126, 36); + $this->setOuterLayout($outerLayout); + + $this->assign('BODY.CLASS', quot(\PXRegistry::getRequest()->getArea())); + } + + protected function assignVersion() + { + $user = \PXRegistry::getUser(); + $version = ($user->isAuthed()) + ? PP_VERSION + : ''; + + $this->assign('OUTER.VERSION', $version); + } + + /** + * Retrieves all flash messages from session, makes html and + * assigns it to layout. + */ + public function assignFlashes() + { + $htmlFlashes = ''; + $session = \PXRegistry::getSession(); + + foreach ($session->getFlashBag()->all() as $type => $messages) { + foreach ($messages as $message) { + $htmlFlashes .= sprintf( + '
×%s
', + $type, + $message + ); + } + } + + $this->assign('FLASHES', $htmlFlashes); + } + + /** + * @param null|string $title + * @return $this + */ + public function assignTitle($title = null) + { + $title = ((is_null($title) || !mb_strlen(trim($title))) + ? '' + : mb_strtr($title, ['<' => '<', '>' => '>'])); + + $this->assign("OUTER.TITLE", $title); + + return $this; + } + + public function assignError($label, $errorText) + { + $this->assign($label, '

' . $errorText . '

'); + } + + public function assignContentControls($label, $selectedSid, $allowedFormats): void + { + throw new \Exception("please use AdminHtmlLayout::AssignControls"); + } + + public function assignControls($label, $selectedSid, $allowedFormats) + { + $this->clear($label); + $this->appendControls($label, $selectedSid, $allowedFormats); + } + + public function appendControls($label, $selectedSid, $allowedFormats) + { + foreach ($allowedFormats as $format) { + $button = new \PXControlButton($this->types[$format]->title); + $button->setClickCode('AddContent(\'' . $format . '\', ' . (int)$selectedSid . ')'); + $button->setClass('add'); + + $button->addToParent($label); + } + } + + public function _makeContextMenu($selectedSid, $allowedFormats) + { + $html = ''; + + if (sizeof($allowedFormats)) { + $html .= ' onContextMenu="Context(event, \'add\', \'' . quot(quot($selectedSid), false) . '\''; + + foreach ($this->types as $k => $v) { + if (in_array($k, $allowedFormats)) { + $html .= ' , \'' . $k . '\', \'' . $v->title . '\''; + } + } + + $html .= '); return false;" '; + } + + return $html; + } + + public function assignContextMenu($label, $selectedSid, $allowedFormats) + { + $this->assign($label, $this->_makeContextMenu($selectedSid, $allowedFormats)); + } + + public function appendContextMenu($label, $selectedSid, $allowedFormats) + { + // store formats and labels for each parent + // some times there are same parents so we can share context menus + static $stored = []; + if (!isset($stored[$selectedSid])) { + $stored[$selectedSid] = [ + 'labels' => [], + 'formats' => [], + ]; + } + + // append to label + $collection = &$stored[$selectedSid]; + $collection['labels'][] = $label; + $collection['formats'] = array_merge($collection['formats'], $allowedFormats); + + // draw it for each label (each space) + foreach ($collection['labels'] as $_label) { + $this->assign($_label, $this->_makeContextMenu($selectedSid, $collection['formats'])); + } + } + + public function appendTable($label, $objectFormat, $table, $selected = null, $varToTitle = null, $page = 1, $objectsOnPage = 0, $count = 0, $withLinks = true, $parentPathname = null) + { + $htmltable = new \PXAdminTable($table, $this->types[$objectFormat], $this->getData); + $htmltable->setCaption($this->types[$objectFormat]->title . '(' . $count . ')'); + + $pager = new \PxAdminPager($page, $objectsOnPage, $count, $this->types[$objectFormat], $this->getData); + $htmltable->setPosition($pager->getPosition()); + + $this->append($label, $htmltable->html() . $pager->html()); + } + + public function appendUserTable($label, $objectFormat, $title, $table, &$userClassName, $userFuncName, $page = 1, $objectsOnPage = 0, $count = 0) + { + $this->Append($label, '

' . $title . ' (' . $count . ')

'); + $html = null; + $page = $page ?: 1; // ? + + if (!(is_countable($table) ? count($table) : 0)) { + $html = ''; + $this->Append($label, $html); + return; + } + + $html .= call_user_func([$userClassName, $userFuncName], 'header'); + foreach ($table as $rowPos => $row) { + $html .= call_user_func([$userClassName, $userFuncName], 'row', $row); + } + $html .= call_user_func([$userClassName, $userFuncName], 'footer'); + + if ($count > $objectsOnPage && $objectsOnPage > 0) { + $html .= '
'; + $html .= 'Страницы: '; + + $allPages = ceil($count / $objectsOnPage) + 1; + $start = (ceil($page / 10) - 1) * 10 + 1; + $max = $start + 10; + + if ($max > $allPages) { + $max = $allPages; + } + + if ($page > 10) { + $prev = (ceil($start / 10) - 1) * 10 - 9; + } + + $last = $allPages - $start - 10; + + if ($last > 0) { + $next = (ceil($start / 10) - 1) * 10 + 11; + } + + if (isset($prev)) { + $html .= ''; + $html .= 'Страница ' . $prev . ''; + $html .= ''; + } + + for ($i = $start; $i < $max; $i++) { + $html .= '' . $i . ' '; + } + + if (isset($next) && $next > 0) { + $html .= ''; + $html .= 'Страница ' . $next . ''; + $html .= ''; + } + + $html .= '
'; + } + $this->Append($label, $html); + } + + public function assignKeyValueList($label, $list, $selected, $varName = 'sid') + { + parent::AssignKeyValueList($label, $list, $varName, $selected); + } + + public function assignJS($pathToScript) + { + $this->_renderScript($pathToScript, ':js', true); + } + + public function assignInlineJS($scriptBody, $uniq = true) + { + $this->_renderScript($scriptBody, ':inline_js', $uniq); + } + + public function assignCSS($pathToScript) + { + $this->_renderScript($pathToScript, ':css', true); + } + + public function assignInlineCSS($scriptBody) + { + $this->_renderScript($scriptBody, ':inline_css', true); + } + + public function _renderScript($body, $template, $singleton) + { + $hash = md5((string) $body); + if (!mb_strlen((string) $body) || ($singleton && isset($this->_scripts[$hash]))) { + return false; + } + + $this->append($this->_scriptsTemplates[$template][':area'], sprintf($this->_scriptsTemplates[$template][':proto'], $body)); + + return $singleton ? ($this->_scripts[$hash] = true) : true; + } + + public function template($filename, $label = null) + { + $this->assignVersion(); + + return parent::template($filename, $label); + } + + public function notSetAllowedChilds($label, $format, $id) + { + $format = \PXRegistry::getTypes($format); + + $this->assign( + $label, + <<Для добавления в раздел информации необходимо указать разрешенные форматы
    @@ -301,6 +300,6 @@ public function notSetAllowedChilds($label, $format, $id)
HTML - ); - } + ); + } } diff --git a/src/Lib/Html/Layout/LayoutAbstract.php b/src/Lib/Html/Layout/LayoutAbstract.php index 0393be3b..68b522a5 100644 --- a/src/Lib/Html/Layout/LayoutAbstract.php +++ b/src/Lib/Html/Layout/LayoutAbstract.php @@ -14,485 +14,492 @@ abstract class LayoutAbstract implements LayoutInterface { - - private $html = ''; - private $labels = []; - - /** @var array */ - protected $template_dirs; - - public $getData; - public $outerLayout; - - public function __construct() - { - $this->getData = []; - $this->template_dirs = [ - LOCALPATH . '/templates/admin/', - PPCOREPATH . '/templates/admin/', - ]; - } - - /** - * @param $template - * @return $this - */ - public function setOuterLayout($template) - { - $this->outerLayout = $template; - $this->html = $this->template($template . '.tmpl'); - return $this; - } - - public function template($filename, $label = null) - { - foreach ($this->template_dirs as $dir) { - if (file_exists($dir . $filename)) { - $html = file_get_contents($dir . $filename); - - if (is_string($label)) { - $this->append($label, $html); - } - - return $html; - } - } - - FatalError('Template ' . $filename . ' does not exists'); - } - - /** - * {@inheritdoc} - */ - public function setApp(\PXApplication $app) - { - return $this; - } - - /** - * {@inheritdoc} - */ - public function setLang($lang = 'rus') - { - return $this; - } - - /** - * {@inheritdoc} - */ - public function getLang() - { - return null; - } - - /** - * {@inheritdoc} - */ - public function getSmarty() - { - return null; - } - - /** - * {@inheritdoc} - */ - public function getIndexTemplate() - { - return null; - } - - /** - * {@inheritdoc} - */ - public function setContent($content) - { - return $this; - } - - /** - * {@inheritdoc} - */ - public function getContent() - { - return null; - } - - public function _arrayToAttrs($array) - { - if (!sizeof($array)) { - return ''; - } - - $attrs = [' ']; - - foreach ($array as $k => $v) { - $attrs[] = $k . '="' . $v . '"'; - } - - return implode(' ', $attrs); - } - - /** - * @param $table - * @return $this - * @deprecated - */ - public function setInnerLayout($table) - { - $html = '
'; - - foreach ($table as $rk => $row) { - $html .= ''; - - foreach ($row as $ck => $col) { - $td = []; - - if (!empty($col[0])) $td['width'] = $col[0]; - if (!empty($col[1])) $td['style'] = 'height:' . $col[1]; - if (!empty($col[2])) $td['colspan'] = $col[2]; - if (!empty($col[3])) $td['rowspan'] = $col[3]; - - $divClass = !empty($col[1]) ? ' class="content"' : ''; - - $html .= '_arrayToAttrs($td) . '>'; - $html .= '
{INNER.' . $ck . '.' . $rk . '}
'; - $html .= ''; - } - - $html .= '
'; - } - - $html .= '
'; - - $this->assign('OUTER.MAINAREA', $html); - return $this; - } - - /** - * @return $this - */ - public function setOneColumn() - { - $this->setSimpleInnerLayout(['100%'], ['100%', '']); - return $this; - } - - /** - * @return $this - */ - public function setTwoColumns($equalWidth = false) - { - $widths = $equalWidth ? ['50%', '50%'] : ['25%', '75%']; - $this->setSimpleInnerLayout($widths, ['100%', '']); - return $this; - } - - /** - * @return $this - */ - public function setThreeColumns() - { - $this->setSimpleInnerLayout(['25%', '40%', '35%'], ['100%', '']); - return $this; - } - - /** - * @param $widthArray - * @param $heightArray - * @return $this - * @deprecated - */ - public function setSimpleInnerLayout($widthArray, $heightArray) - { - $table = []; - - foreach ($heightArray as $hk => $height) { - $table[$hk] = []; - - foreach ($widthArray as $wk => $width) { - $table[$hk][$wk] = [$width, $height, null, null]; - } - } - - $this->setInnerLayout($table); - return $this; - } - - /** - * @param $action - * @param $method - * @param $enctype - * @param bool $autoHeight - * @return $this - */ - public function setOuterForm($action, $method, $enctype, $autoHeight = false) - { - $this->assign('OUTER.FORMBEGIN', '
'); - $this->assign('OUTER.FORMEND', '
'); - return $this; - } - - /** - * @param $image - * @param $text - * @param $width - * @param $height - * @param string $href - * @return $this - */ - public function setOuterLogo($image, $text, $width, $height, $href = '') - { - if (!empty($image)) { - $html = ''; - } else { - $pad = round($height - 22) / 2; - $html = ''; - } - - $this->assign('OUTER.LOGO', $html); - return $this; - } - - /** - * @param $menuItems - * @param $current - * @param string $getParam - * @param bool $buildHref - * @return $this - */ - public function setMenu($menuItems, $current, $getParam = 'area', $buildHref = true) - { - $menu = new \PXWidgetMenuTabbed(); - - $menu->items = $menuItems; - $menu->selected = $current; - $menu->varName = $getParam; - $menu->buildHref = $buildHref; - - $this->assign('OUTER.MENU', $menu); - return $this; - } - - /** - * @param $href - * @return $this - */ - public function setLogoutForm($href) - { - $this->assign('OUTER.EXIT', $this->template('form-logout.tmpl')); - $this->assign('LOGOUT.HREF', $href); - $this->assign('USER.TITLE', \PXRegistry::getUser()->getTitle()); - return $this; - } - - /** - * @param $formAction - * @param $formMethod - * @param $namesArray - * @param $valuesArray - * @return $this - */ - public function setLoginForm($formAction, $formMethod, $namesArray, $valuesArray) - { - $this->assign('OUTER.MAINAREA', $this->template('form-login.tmpl')); - - $this->assign('LOGIN.FORMACTION', $formAction); - $this->assign('LOGIN.FORMMETHOD', $formMethod); - - $this->assign('LOGIN.LOGINNAME', getFromArray($namesArray, 'login')); - $this->assign('LOGIN.PASSWDNAME', getFromArray($namesArray, 'passwd')); - $this->assign('LOGIN.LOGINVALUE', quot(getFromArray($valuesArray, 'login'))); - $this->assign('LOGIN.REFERERNAME', getFromArray($namesArray, 'referer')); - $this->assign('LOGIN.REFERERVALUE', quot(getFromArray($valuesArray, 'referer'))); - $this->assign('LOGIN.AREANAME', getFromArray($namesArray, 'area')); - $this->assign('LOGIN.AREAVALUE', getFromArray($valuesArray, 'area')); - $this->assign('CAPTCHA.KEY', getFromArray($valuesArray, 'captchaKey')); - $this->assign('CAPTCHA.NOTE', getFromArray($valuesArray, 'captchaNote')); - $this->assign('CAPTCHA.KEYNAME', getFromArray($namesArray, 'captchaKey')); - $this->assign('CAPTCHA.VALNAME', getFromArray($namesArray, 'captchaVal')); - return $this; - } - - public function setGetVarToSave($key, $value) - { - $this->getData[$key] = $value; - $this->assign(strtoupper($key), $value); - } - - public function clearGetVar($key) - { - unset($this->getData[$key]); - $this->assign(strtoupper($key), ''); - } - - public function clear($label) - { - $this->assign($label, null); - } - - /** - * alias for assign - * - * @param $label - * @param $value - * @return $this - * @deprecated - */ - public function set($label, &$value) - { - $this->labels[$label] = []; - $this->add($label, $value); - return $this; - } - - /** - * {@inheritdoc} - */ - public function assign($label, $value) - { - $refValue = $value; - $this->set($label, $refValue); - - return $this; - } - - public function isWidget($value) - { - return is_object($value) && $value instanceof \PXAdminWidgetIF; - } - - /** - * alias for append - * - * @param $label - * @param $value - * @return $this - * @deprecated - */ - public function add($label, &$value) - { - if (!isset($this->labels[$label]) || !is_array($this->labels[$label])) { - $this->labels[$label] = []; - } - - switch (true) { - case $this->isWidget($value): - $this->labels[$label][] =& $value; - break; - - case is_scalar($value): - $this->labels[$label][] = (string)$value; - break; - - case is_array($value): - $this->labels[$label][] = (array)$value; - break; - - case is_null($value): - // pass - break; - - default: - FatalError('Undefined type for layout content ' . var_export($value, true)); - break; - } - - return $this; - } - - /** - * @param $label - * @param $value - * @return $this - */ - public function append($label, $value) - { - $refValue = $value; - $this->add($label, $refValue); - return $this; - } - - /** - * @param $label - * @param $list - * @param $varName - * @param $selected - * @return $this - */ - public function assignKeyValueList($label, $list, $varName, $selected) - { - $list = new \PXAdminList($list); - - $list->setVarName($varName); - $list->setSelected($selected); - $list->setGetData($this->getData); - - $this->assign($label, $list); - return $this; - } - - /** - * @return string - */ - public function html() - { - // widgets to html - // replace labels to html - // WARNING: DO NOT use foreach() for $this->labels, because $widget->html() calls - // can append data to $this->labels! array_walk() acts like deprecated each() here - array_walk($this->labels, function ($widgets, $label) { - if (strpos($this->html, '{' . $label . '}') === false) { - return; - } - $html = ''; - foreach ($widgets as $widget) { - $html .= $this->isWidget($widget) ? $widget->html() : $widget; - } - - $this->html = str_replace('{' . $label . '}', $html, $this->html); - }); - - // delete labels without content - $this->html = preg_replace('/\{(?>\w[\w\.]*(?!\.))\}/', '', $this->html); - - return $this->html; - } - - public function flush($charset = null) - { - $result = $this->html(); - $response = Response::getInstance(); - $response->send($result); - } - - // static - public static function _buildHref($key, $value) - { - return static::buildHref($key, $value); - } - - // static - public static function buildHref($key, $value, $getData = [], $href = "?") - { - $layoutData = \PXRegistry::getLayout()->getData; - $getData = array_merge($layoutData, $getData); - - parse_str($href, $href_vars); - - foreach ($getData as $k => $v) { - $flag = false; - $flag = $flag || (is_array($v) && count($v)); - $flag = $flag || (is_string($v) && strval($v) !== ""); - $flag = $flag || (is_numeric($v)); - $flag = $flag && ($k != $key && empty($href_vars[$k])); - - if ($flag) { - $href = appendParamToUrl($href, $k, $v); - } - } - - $href = parse_url(appendParamToUrl($href, $key, $value)); - return @"?{$href['query']}"; - } + private $html = ''; + private array $labels = []; + + /** @var array */ + protected $template_dirs; + + public $getData; + public $outerLayout; + + public function __construct() + { + $this->getData = []; + $this->template_dirs = [ + LOCALPATH . '/templates/admin/', + PPCOREPATH . '/templates/admin/', + ]; + } + + /** + * @param $template + * @return $this + */ + public function setOuterLayout($template) + { + $this->outerLayout = $template; + $this->html = $this->template($template . '.tmpl'); + return $this; + } + + public function template($filename, $label = null) + { + foreach ($this->template_dirs as $dir) { + if (file_exists($dir . $filename)) { + $html = file_get_contents($dir . $filename); + + if (is_string($label)) { + $this->append($label, $html); + } + + return $html; + } + } + + FatalError('Template ' . $filename . ' does not exists'); + } + + /** + * {@inheritdoc} + */ + public function setApp(\PXApplication $app) + { + return $this; + } + + /** + * {@inheritdoc} + */ + public function setLang($lang = 'rus') + { + return $this; + } + + /** + * {@inheritdoc} + */ + public function getLang() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getSmarty() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getIndexTemplate() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function setContent($content) + { + return $this; + } + + /** + * {@inheritdoc} + */ + public function getContent() + { + return null; + } + + public function _arrayToAttrs($array) + { + if (!sizeof($array)) { + return ''; + } + + $attrs = [' ']; + + foreach ($array as $k => $v) { + $attrs[] = $k . '="' . $v . '"'; + } + + return implode(' ', $attrs); + } + + /** + * @param $table + * @return $this + * @deprecated + */ + public function setInnerLayout($table) + { + $html = ''; + + foreach ($table as $rk => $row) { + $html .= ''; + + foreach ($row as $ck => $col) { + $td = []; + + if (!empty($col[0])) { + $td['width'] = $col[0]; + } + if (!empty($col[1])) { + $td['style'] = 'height:' . $col[1]; + } + if (!empty($col[2])) { + $td['colspan'] = $col[2]; + } + if (!empty($col[3])) { + $td['rowspan'] = $col[3]; + } + + $divClass = !empty($col[1]) ? ' class="content"' : ''; + + $html .= '_arrayToAttrs($td) . '>'; + $html .= '
{INNER.' . $ck . '.' . $rk . '}
'; + $html .= ''; + } + + $html .= '
'; + } + + $html .= '
'; + + $this->assign('OUTER.MAINAREA', $html); + return $this; + } + + /** + * @return $this + */ + public function setOneColumn() + { + $this->setSimpleInnerLayout(['100%'], ['100%', '']); + return $this; + } + + /** + * @return $this + */ + public function setTwoColumns($equalWidth = false) + { + $widths = $equalWidth ? ['50%', '50%'] : ['25%', '75%']; + $this->setSimpleInnerLayout($widths, ['100%', '']); + return $this; + } + + /** + * @return $this + */ + public function setThreeColumns() + { + $this->setSimpleInnerLayout(['25%', '40%', '35%'], ['100%', '']); + return $this; + } + + /** + * @param $widthArray + * @param $heightArray + * @return $this + * @deprecated + */ + public function setSimpleInnerLayout($widthArray, $heightArray) + { + $table = []; + + foreach ($heightArray as $hk => $height) { + $table[$hk] = []; + + foreach ($widthArray as $wk => $width) { + $table[$hk][$wk] = [$width, $height, null, null]; + } + } + + $this->setInnerLayout($table); + return $this; + } + + /** + * @param $action + * @param $method + * @param $enctype + * @param bool $autoHeight + * @return $this + */ + public function setOuterForm($action, $method, $enctype, $autoHeight = false) + { + $this->assign('OUTER.FORMBEGIN', '
'); + $this->assign('OUTER.FORMEND', '
'); + return $this; + } + + /** + * @param $image + * @param $text + * @param $width + * @param $height + * @param string $href + * @return $this + */ + public function setOuterLogo($image, $text, $width, $height, $href = '') + { + if (!empty($image)) { + $html = ''; + } else { + $pad = round($height - 22) / 2; + $html = ''; + } + + $this->assign('OUTER.LOGO', $html); + return $this; + } + + /** + * @param $menuItems + * @param $current + * @param string $getParam + * @param bool $buildHref + * @return $this + */ + public function setMenu($menuItems, $current, $getParam = 'area', $buildHref = true) + { + $menu = new \PXWidgetMenuTabbed(); + + $menu->items = $menuItems; + $menu->selected = $current; + $menu->varName = $getParam; + $menu->buildHref = $buildHref; + + $this->assign('OUTER.MENU', $menu); + return $this; + } + + /** + * @param $href + * @return $this + */ + public function setLogoutForm($href) + { + $this->assign('OUTER.EXIT', $this->template('form-logout.tmpl')); + $this->assign('LOGOUT.HREF', $href); + $this->assign('USER.TITLE', \PXRegistry::getUser()->getTitle()); + return $this; + } + + /** + * @param $formAction + * @param $formMethod + * @param $namesArray + * @param $valuesArray + * @return $this + */ + public function setLoginForm($formAction, $formMethod, $namesArray, $valuesArray) + { + $this->assign('OUTER.MAINAREA', $this->template('form-login.tmpl')); + + $this->assign('LOGIN.FORMACTION', $formAction); + $this->assign('LOGIN.FORMMETHOD', $formMethod); + + $this->assign('LOGIN.LOGINNAME', getFromArray($namesArray, 'login')); + $this->assign('LOGIN.PASSWDNAME', getFromArray($namesArray, 'passwd')); + $this->assign('LOGIN.LOGINVALUE', quot(getFromArray($valuesArray, 'login'))); + $this->assign('LOGIN.REFERERNAME', getFromArray($namesArray, 'referer')); + $this->assign('LOGIN.REFERERVALUE', quot(getFromArray($valuesArray, 'referer'))); + $this->assign('LOGIN.AREANAME', getFromArray($namesArray, 'area')); + $this->assign('LOGIN.AREAVALUE', getFromArray($valuesArray, 'area')); + $this->assign('CAPTCHA.KEY', getFromArray($valuesArray, 'captchaKey')); + $this->assign('CAPTCHA.NOTE', getFromArray($valuesArray, 'captchaNote')); + $this->assign('CAPTCHA.KEYNAME', getFromArray($namesArray, 'captchaKey')); + $this->assign('CAPTCHA.VALNAME', getFromArray($namesArray, 'captchaVal')); + return $this; + } + + public function setGetVarToSave($key, $value) + { + $this->getData[$key] = $value; + $this->assign(strtoupper((string) $key), $value); + } + + public function clearGetVar($key) + { + unset($this->getData[$key]); + $this->assign(strtoupper((string) $key), ''); + } + + public function clear($label) + { + $this->assign($label, null); + } + + /** + * alias for assign + * + * @param $label + * @param $value + * @return $this + * @deprecated + */ + public function set($label, &$value) + { + $this->labels[$label] = []; + $this->add($label, $value); + return $this; + } + + /** + * {@inheritdoc} + */ + public function assign($label, $value) + { + $refValue = $value; + $this->set($label, $refValue); + + return $this; + } + + public function isWidget($value) + { + return is_object($value) && $value instanceof \PXAdminWidgetIF; + } + + /** + * alias for append + * + * @param $label + * @param $value + * @return $this + * @deprecated + */ + public function add($label, &$value) + { + if (!isset($this->labels[$label]) || !is_array($this->labels[$label])) { + $this->labels[$label] = []; + } + + switch (true) { + case $this->isWidget($value): + $this->labels[$label][] = & $value; + break; + + case is_scalar($value): + $this->labels[$label][] = (string)$value; + break; + + case is_array($value): + $this->labels[$label][] = (array)$value; + break; + + case is_null($value): + // pass + break; + + default: + FatalError('Undefined type for layout content ' . var_export($value, true)); + break; + } + + return $this; + } + + /** + * @param $label + * @param $value + * @return $this + */ + public function append($label, $value) + { + $refValue = $value; + $this->add($label, $refValue); + return $this; + } + + /** + * @param $label + * @param $list + * @param $varName + * @param $selected + * @return $this + */ + public function assignKeyValueList($label, $list, $varName, $selected) + { + $list = new \PXAdminList($list); + + $list->setVarName($varName); + $list->setSelected($selected); + $list->setGetData($this->getData); + + $this->assign($label, $list); + return $this; + } + + /** + * @return string + */ + public function html() + { + // widgets to html + // replace labels to html + // WARNING: DO NOT use foreach() for $this->labels, because $widget->html() calls + // can append data to $this->labels! array_walk() acts like deprecated each() here + array_walk($this->labels, function ($widgets, $label) { + if (!str_contains((string) $this->html, '{' . $label . '}')) { + return; + } + $html = ''; + foreach ($widgets as $widget) { + $html .= $this->isWidget($widget) ? $widget->html() : $widget; + } + + $this->html = str_replace('{' . $label . '}', $html, (string) $this->html); + }); + + // delete labels without content + $this->html = preg_replace('/\{(?>\w[\w\.]*(?!\.))\}/', '', (string) $this->html); + + return $this->html; + } + + public function flush($charset = null) + { + $result = $this->html(); + $response = Response::getInstance(); + $response->send($result); + } + + // static + public static function _buildHref($key, $value) + { + return static::buildHref($key, $value); + } + + // static + public static function buildHref($key, $value, $getData = [], $href = "?") + { + $layoutData = \PXRegistry::getLayout()->getData; + $getData = array_merge($layoutData, $getData); + + parse_str((string) $href, $href_vars); + + foreach ($getData as $k => $v) { + $flag = false; + $flag = $flag || (is_array($v) && count($v)); + $flag = $flag || (is_string($v) && strval($v) !== ""); + $flag = $flag || (is_numeric($v)); + $flag = $flag && ($k != $key && empty($href_vars[$k])); + + if ($flag) { + $href = appendParamToUrl($href, $k, $v); + } + } + + $href = parse_url((string) appendParamToUrl($href, $key, $value)); + return @"?{$href['query']}"; + } } diff --git a/src/Lib/Html/Layout/LayoutInterface.php b/src/Lib/Html/Layout/LayoutInterface.php index 6c468a56..f1ab9974 100644 --- a/src/Lib/Html/Layout/LayoutInterface.php +++ b/src/Lib/Html/Layout/LayoutInterface.php @@ -11,56 +11,54 @@ */ interface LayoutInterface { + /** + * @param string $name + * @param string $value + * @return $this + */ + public function assign($name, $value); - /** - * @param string $name - * @param string $value - * @return $this - */ - public function assign($name, $value); + /** + * @return $this + */ + public function setApp(\PXApplication $app); - /** - * @param \PXApplication $app - * @return $this - */ - public function setApp(\PXApplication $app); + /** + * TODO: should be refactored to setLangCode + * + * @param string $lang + * @return $this + */ + public function setLang($lang = 'rus'); - /** - * TODO: should be refactored to setLangCode - * - * @param string $lang - * @return $this - */ - public function setLang($lang = 'rus'); + /** + * + * @return \PXUserHTMLLang + */ + public function getLang(); - /** - * - * @return \PXUserHTMLLang - */ - public function getLang(); + /** + * @return \Smarty + */ + public function getSmarty(); - /** - * @return \Smarty - */ - public function getSmarty(); + /** + * @return string + */ + public function getIndexTemplate(); - /** - * @return string - */ - public function getIndexTemplate(); + /** + * Null layout in action/json handler Null layout is used, so it should be in interface + * + * @param object + * @return $this + */ + public function setContent($content); - /** - * Null layout in action/json handler Null layout is used, so it should be in interface - * - * @param object - * @return $this - */ - public function setContent($content); - - /** - * Null layout in action/json handler Null layout is used, so it should be in interface - * - * @return null|\PPBEMJSONContent - */ - public function getContent(); + /** + * Null layout in action/json handler Null layout is used, so it should be in interface + * + * @return null|\PPBEMJSONContent + */ + public function getContent(); } diff --git a/src/Lib/Html/Layout/NullLayout.php b/src/Lib/Html/Layout/NullLayout.php index 7d3476e9..fc9ca7c8 100644 --- a/src/Lib/Html/Layout/NullLayout.php +++ b/src/Lib/Html/Layout/NullLayout.php @@ -8,7 +8,6 @@ */ class NullLayout implements LayoutInterface { - /** * {@inheritdoc} */ diff --git a/src/Lib/Http/Response.php b/src/Lib/Http/Response.php index 19cbc6f7..a3bc49fc 100644 --- a/src/Lib/Http/Response.php +++ b/src/Lib/Http/Response.php @@ -14,326 +14,316 @@ final class Response { - /** @var self */ - private static $instance; - - /** @var HttpResponse */ - private $httpResponse; - - /** - * Response constructor. - */ - public function __construct() - { - $this->httpResponse = new HttpResponse(); - - try { - $app = PXRegistry::getApp(); // APP may not be initialized yet and throws exception - $charset = $app->getProperty('OUTPUT_CHARSET', DEFAULT_CHARSET); - $cacheTime = $app->getProperty('PP_RESPONSE_CACHE_EXPIRATION', 3600); - - } catch (Exception $e) { - $charset = DEFAULT_CHARSET; - $cacheTime = 3600; - } - - $this->cache($cacheTime); - $this->setContentType('text/html', $charset); - } - - /** - * @param int $timeOut - * @param null $xae - * @return $this - */ - public function cache($timeOut = 3600, $xae = null): self - { - $this->httpResponse - ->setPrivate() - ->setMaxAge((int)$timeOut); - - $this->addHeader('X-Accel-Expires', intval($xae ?? $timeOut)); - - return $this; - } - - /** - * @param string $name - * @param string $value - * @return $this - */ - public function addHeader(string $name, string $value): self - { - $this->httpResponse->headers->set($name, $value); - return $this; - } - - /** - * @param string $contentType - * @param string|null $charset - * @return $this - */ - public function setContentType(string $contentType, ?string $charset = null): self - { - $this->httpResponse->headers->set('Content-Type', $contentType); - - if (mb_strlen($charset)) { - $this->httpResponse->setCharset($charset); - } - - return $this; - } - - /** - * @return static - */ - public static function getInstance(): self - { - if (is_null(static::$instance)) { - static::$instance = new self(); - } - - return static::$instance; - } - - /** - * @param string $url - * @param string|int|null $cacheTimeOut - * @param int $statusCode - */ - public function redirect(string $url, $cacheTimeOut = null, int $statusCode = HttpResponse::HTTP_MOVED_PERMANENTLY): void - { - if (ini_get('display_errors') && PXErrorReporter::hasErrors()) { - exit(); - } - - if (!is_null($cacheTimeOut)) { - $this->cache($cacheTimeOut); - } else { - $this->dontCache(); - } - - $redirectResponse = new RedirectResponse($url, $statusCode, $this->httpResponse->headers->all()); - - $redirectResponse->send(); - - exit(); - } - - /** - * @return $this - */ - public function dontCache(): self - { - $this->httpResponse->setPublic(); - $this->addHeader('X-Accel-Expires', 0) - ->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate') - ->addHeader('Expires', DateUnixToGMT()); - - return $this; - } - - /** - * @param string|null $content - */ - public function send(?string $content = null): void - { - if (mb_strlen($content)) { - // NOTICE: no mb_ here - $this->setLength(strlen($content)); - } - $this->httpResponse->setContent($content); - $this->httpResponse->send(); - } - - /** - * @param int $length - * @return $this - */ - public function setLength(int $length): self { - $this->addHeader('Content-Length', $length); - return $this; - } - - /** - * @return $this - */ - public function setOk(): self - { - $this->httpResponse->setStatusCode(HttpResponse::HTTP_OK); - return $this; - } - - /** - * @return $this - */ - public function notFound(): self - { - $this->httpResponse->setStatusCode(HttpResponse::HTTP_NOT_FOUND); - return $this; - } - - // Cache management - - /** - * @return $this - */ - public function forbidden(): self - { - $this->httpResponse->setStatusCode(HttpResponse::HTTP_FORBIDDEN); - return $this; - } - - /** - * @return $this - */ - public function unavailable(): self - { - $this->httpResponse->setStatusCode(HttpResponse::HTTP_SERVICE_UNAVAILABLE); - $this->noCache(); - return $this; - } - - /** - * @return $this - */ - public function noCache(): self - { // not sure about caching behaviour, make upstream server responsible for this - $this->removeHeader('X-Accel-Expires') - ->removeHeader('Cache-Control') - ->removeHeader('Expires'); - - return $this; - } - - /** - * @param string $name - * @return $this - */ - public function removeHeader(string $name): self - { - $this->httpResponse->headers->remove($name); - return $this; - } - - /** - * @return bool - */ - public function isError(): bool - { - return $this->httpResponse->isClientError() || $this->httpResponse->isServerError(); - } - - /** - * @param int $code - * @return $this - */ - public function setStatus(int $code): self - { - $this->httpResponse->setStatusCode($code); - return $this; - } - - /** - * @param string $charset - * @return $this - */ - public function setCharset(string $charset): self - { - $this->httpResponse->setCharset($charset); - return $this; - } - - /** - * @param string $filename - * @param string|null $contentType - * @param string $dispositionType - * @param string|null $charset - * @return $this - */ - public function downloadFile(string $filename, ?string $contentType = null, - string $dispositionType = ResponseHeaderBag::DISPOSITION_ATTACHMENT, - ?string $charset = null): self - { - if (mb_strlen($contentType)) { - $this->setContentType($contentType, $charset); - } - - $disposition = $this->httpResponse->headers->makeDisposition($dispositionType, $filename); - - $this->httpResponse->headers->set('Content-Disposition', $disposition); - - return $this; - } - - /** - * @param null $jsonData - * @param string|null $contentType - * @param string|null $charset - */ - public function sendJson($jsonData = null, ?string $contentType = null, ?string $charset = null): void - { - if (mb_strlen($contentType)) { - $this->setContentType($contentType, $charset); - } else { - $this->httpResponse->headers->remove('Content-Type'); // JsonResponse class automatic set proper content type - } - - // TODO: add JsonResponse callback support? - $jsonResponse = new JsonResponse($jsonData, $this->httpResponse->getStatusCode(), $this->httpResponse->headers->all()); - $jsonResponse->headers->set('Content-Length', strlen($jsonResponse->getContent())); - $jsonResponse->send(); - } - - /** - * @param callable $callback - */ - public function sendStream(callable $callback): void - { - $streamedResponse = new StreamedResponse($callback, $this->httpResponse->getStatusCode(), $this->httpResponse->headers->all()); - $streamedResponse->send(); - } - - /** - * Set cookie - * - * (Don't send cookie, if headers already sent) - * - * @param string $name - * @param mixed $value - * @param int|null $expire - * @param string|null $domain - * @param string $path - * @param bool $secure - * @param bool $httpOnly - * @param bool $raw - * @param string|null $sameSite - * @return bool - * @noinspection PhpTooManyParametersInspection - */ - public function setCookie(string $name, $value, ?int $expire = null, ?string $domain = null, string $path = '/', - bool $secure = false, bool $httpOnly = false, bool $raw = false, ?string $sameSite = null) - { - if (is_array($value) || is_object($value)) { - $value = serialize($value); - } - - if (!is_numeric($expire)) { - $expire = 0; - } - - $this->httpResponse->headers->setCookie(new Cookie($name, $value, $expire, $path, $domain, $secure, - $httpOnly, $raw, $sameSite)); - - return true; - } - - /** - * Get access to Symfony Response instance for edge cases - * @return HttpResponse - */ - public function getBaseResponse(): HttpResponse - { - return $this->httpResponse; - } + private static ?\PP\Lib\Http\Response $instance = null; + + /** @var HttpResponse */ + private readonly HttpResponse $httpResponse; + + /** + * Response constructor. + */ + public function __construct() + { + $this->httpResponse = new HttpResponse(); + + try { + $app = PXRegistry::getApp(); // APP may not be initialized yet and throws exception + $charset = $app->getProperty('OUTPUT_CHARSET', DEFAULT_CHARSET); + $cacheTime = $app->getProperty('PP_RESPONSE_CACHE_EXPIRATION', 3600); + + } catch (Exception) { + $charset = DEFAULT_CHARSET; + $cacheTime = 3600; + } + + $this->cache($cacheTime); + $this->setContentType('text/html', $charset); + } + + /** + * @param int $timeOut + * @param null $xae + * @return $this + */ + public function cache($timeOut = 3600, $xae = null): self + { + $this->httpResponse + ->setPrivate() + ->setMaxAge((int)$timeOut); + + $this->addHeader('X-Accel-Expires', intval($xae ?? $timeOut)); + + return $this; + } + + /** + * @return $this + */ + public function addHeader(string $name, string $value): self + { + $this->httpResponse->headers->set($name, $value); + return $this; + } + + /** + * @return $this + */ + public function setContentType(string $contentType, ?string $charset = null): self + { + $this->httpResponse->headers->set('Content-Type', $contentType); + + if (mb_strlen($charset)) { + $this->httpResponse->setCharset($charset); + } + + return $this; + } + + public static function getInstance(): self + { + if (is_null(static::$instance)) { + static::$instance = new self(); + } + + return static::$instance; + } + + /** + * @param string|int|null $cacheTimeOut + */ + public function redirect(string $url, $cacheTimeOut = null, int $statusCode = HttpResponse::HTTP_MOVED_PERMANENTLY): void + { + if (ini_get('display_errors') && PXErrorReporter::hasErrors()) { + exit(); + } + + if (!is_null($cacheTimeOut)) { + $this->cache($cacheTimeOut); + } else { + $this->dontCache(); + } + + $redirectResponse = new RedirectResponse($url, $statusCode, $this->httpResponse->headers->all()); + + $redirectResponse->send(); + + exit(); + } + + /** + * @return $this + */ + public function dontCache(): self + { + $this->httpResponse->setPublic(); + $this->addHeader('X-Accel-Expires', 0) + ->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate') + ->addHeader('Expires', DateUnixToGMT()); + + return $this; + } + + public function send(?string $content = null): void + { + if (mb_strlen($content)) { + // NOTICE: no mb_ here + $this->setLength(strlen($content)); + } + $this->httpResponse->setContent($content); + $this->httpResponse->send(); + } + + /** + * @return $this + */ + public function setLength(int $length): self + { + $this->addHeader('Content-Length', $length); + return $this; + } + + /** + * @return $this + */ + public function setOk(): self + { + $this->httpResponse->setStatusCode(HttpResponse::HTTP_OK); + return $this; + } + + /** + * @return $this + */ + public function notFound(): self + { + $this->httpResponse->setStatusCode(HttpResponse::HTTP_NOT_FOUND); + return $this; + } + + // Cache management + + /** + * @return $this + */ + public function forbidden(): self + { + $this->httpResponse->setStatusCode(HttpResponse::HTTP_FORBIDDEN); + return $this; + } + + /** + * @return $this + */ + public function unavailable(): self + { + $this->httpResponse->setStatusCode(HttpResponse::HTTP_SERVICE_UNAVAILABLE); + $this->noCache(); + return $this; + } + + /** + * @return $this + */ + public function noCache(): self + { // not sure about caching behaviour, make upstream server responsible for this + $this->removeHeader('X-Accel-Expires') + ->removeHeader('Cache-Control') + ->removeHeader('Expires'); + + return $this; + } + + /** + * @return $this + */ + public function removeHeader(string $name): self + { + $this->httpResponse->headers->remove($name); + return $this; + } + + public function isError(): bool + { + return $this->httpResponse->isClientError() || $this->httpResponse->isServerError(); + } + + /** + * @return $this + */ + public function setStatus(int $code): self + { + $this->httpResponse->setStatusCode($code); + return $this; + } + + /** + * @return $this + */ + public function setCharset(string $charset): self + { + $this->httpResponse->setCharset($charset); + return $this; + } + + /** + * @return $this + */ + public function downloadFile( + string $filename, + ?string $contentType = null, + string $dispositionType = ResponseHeaderBag::DISPOSITION_ATTACHMENT, + ?string $charset = null + ): self { + if (mb_strlen($contentType)) { + $this->setContentType($contentType, $charset); + } + + $disposition = $this->httpResponse->headers->makeDisposition($dispositionType, $filename); + + $this->httpResponse->headers->set('Content-Disposition', $disposition); + + return $this; + } + + /** + * @param null $jsonData + */ + public function sendJson($jsonData = null, ?string $contentType = null, ?string $charset = null): void + { + if (mb_strlen($contentType)) { + $this->setContentType($contentType, $charset); + } else { + $this->httpResponse->headers->remove('Content-Type'); // JsonResponse class automatic set proper content type + } + + // TODO: add JsonResponse callback support? + $jsonResponse = new JsonResponse($jsonData, $this->httpResponse->getStatusCode(), $this->httpResponse->headers->all()); + $jsonResponse->headers->set('Content-Length', strlen($jsonResponse->getContent())); + $jsonResponse->send(); + } + + public function sendStream(callable $callback): void + { + $streamedResponse = new StreamedResponse($callback, $this->httpResponse->getStatusCode(), $this->httpResponse->headers->all()); + $streamedResponse->send(); + } + + /** + * Set cookie + * + * (Don't send cookie, if headers already sent) + * + * @param string $name + * @param mixed $value + * @param int|null $expire + * @param string|null $domain + * @param string $path + * @param bool $secure + * @param bool $httpOnly + * @param bool $raw + * @param string|null $sameSite + * @return bool + * @noinspection PhpTooManyParametersInspection + */ + public function setCookie( + string $name, + mixed $value, + ?int $expire = null, + ?string $domain = null, + string $path = '/', + bool $secure = false, + bool $httpOnly = false, + bool $raw = false, + ?string $sameSite = null + ) { + if (is_array($value) || is_object($value)) { + $value = serialize($value); + } + + if (!is_numeric($expire)) { + $expire = 0; + } + + $this->httpResponse->headers->setCookie(new Cookie( + $name, + $value, + $expire, + $path, + $domain, + $secure, + $httpOnly, + $raw, + $sameSite + )); + + return true; + } + + /** + * Get access to Symfony Response instance for edge cases + */ + public function getBaseResponse(): HttpResponse + { + return $this->httpResponse; + } } diff --git a/src/Lib/IArrayable.php b/src/Lib/IArrayable.php index 3e8861e9..c31209c3 100644 --- a/src/Lib/IArrayable.php +++ b/src/Lib/IArrayable.php @@ -6,21 +6,20 @@ * Interface IArrayable * @package PP\Lib */ -interface IArrayable { +interface IArrayable +{ + /** + * Converts instance to array + * + * @return mixed + */ + public function toArray(); - /** - * Converts instance to array - * - * @return mixed - */ - public function toArray(); - - /** - * Creates instance from array - * - * @param array $object - * @return mixed - */ - public static function fromArray(array $object); + /** + * Creates instance from array + * + * @return mixed + */ + public static function fromArray(array $object); } diff --git a/src/Lib/Objects/ContentObjectsInterface.php b/src/Lib/Objects/ContentObjectsInterface.php index 04f5dbca..81955442 100644 --- a/src/Lib/Objects/ContentObjectsInterface.php +++ b/src/Lib/Objects/ContentObjectsInterface.php @@ -8,20 +8,19 @@ */ interface ContentObjectsInterface { + public function hasCurrent(); - public function hasCurrent(); + public function getCurrent(); - public function getCurrent(); + public function getAllowedChilds(); - public function getAllowedChilds(); + /** + * @param string $type + * @return bool + */ + public function hasType($type); - /** - * @param string $type - * @return bool - */ - public function hasType($type); + public function getCurrentType(); - public function getCurrentType(); - - public function getLinks(); + public function getLinks(); } diff --git a/src/Lib/PersistentQueue/Job.php b/src/Lib/PersistentQueue/Job.php index fe851988..03903e16 100644 --- a/src/Lib/PersistentQueue/Job.php +++ b/src/Lib/PersistentQueue/Job.php @@ -2,7 +2,7 @@ namespace PP\Lib\PersistentQueue; -use \UnexpectedValueException; +use UnexpectedValueException; use PP\Lib\IArrayable; /** @@ -10,245 +10,258 @@ * * @package PP\Lib\PersistentQueue */ -class Job implements IArrayable { - - /** - * @var string - */ - public const STATE_FRESH = 'fresh'; - - /** - * @var string - */ - public const STATE_FINISHED = 'finished'; - - /** - * @var string - */ - public const STATE_FAILED = 'failed'; - - /** - * @var string - */ - public const STATE_IN_PROGRESS = 'in progress'; - - /** - * @var int - */ - protected $id = 0; - - /** - * @var array - */ - protected $payload; - - /** - * @var string - */ - protected $worker; - - /** - * @var string - */ - protected $state; - - /** - * @var JobResult - */ - protected $resultBag; - - /** - * @var int - */ - protected $ownerId; - - /** - * Job constructor - */ - public function __construct() { - $this->state = static::STATE_FRESH; - $this->resultBag = new JobResult(); - } - - /** - * Converts instance to array. - * - * @return array - */ - public function toArray() { - return [ - 'id' => $this->id, - 'title' => $this->worker, - 'payload' => $this->payload, - 'state' => $this->state, - 'result' => $this->resultBag->toArray(), - 'sys_owner' => $this->ownerId - ]; - } - - /** - * Returns all valid states. - * - * @return array - */ - public static function getValidStates() { - return [ - static::STATE_FRESH, - static::STATE_FAILED, - static::STATE_FINISHED, - static::STATE_IN_PROGRESS - ]; - } - - /** - * Creates instance from array. - * - * @param array $object - * @return static - */ - public static function fromArray(array $object) { - $job = new static(); - $job->setId(getFromArray($object, 'id', 0)); - $workerClass = getFromArray($object, 'title'); - $state = getFromArray($object, 'state', static::STATE_FRESH); - - return $job->setState($state) - ->setPayload(getFromArray($object, 'payload', [])) - ->setWorkerClass($workerClass) - ->setOwnerId(getFromArray($object, 'sys_owner', 0)); - } - - /** - * @param array $payload - * @return $this - */ - public function setPayload(array $payload) { - $this->payload = $payload; - - return $this; - } - - /** - * @return array - */ - public function getPayload() { - return $this->payload; - } - - /** - * @param WorkerInterface $worker - * @return $this - */ - public function setWorker(WorkerInterface $worker) { - $this->setWorkerClass(get_class($worker)); - - return $this; - } - - /** - * @param string $worker - * - * @return $this - */ - public function setWorkerClass($worker) { - $this->worker = $worker; - - return $this; - } - - /** - * @return WorkerInterface - */ - public function getWorker() { - if (!class_exists($this->worker)) { - throw new UnexpectedValueException( - sprintf('Worker class does not exist: %s', $this->worker) - ); - } - - $worker = new $this->worker(); - if (!($worker instanceof WorkerInterface)) { - throw new UnexpectedValueException( - 'Worker class does not implement PP\Lib\PersistentQueue\WorkerInterface' - ); - } - - return $worker; - } - - /** - * @return string - */ - public function getState() { - return $this->state; - } - - /** - * @param $state - * @return $this - */ - public function setState($state) { - $validStates = static::getValidStates(); - if (!in_array($state, $validStates, true)) { - throw new UnexpectedValueException( - sprintf('State is invalid. Valid states: %s', join(', ', $validStates)) - ); - } - - $this->state = $state; - - return $this; - } - - /** - * @return int - */ - public function getId() { - return $this->id; - } - - /** - * @param $id - * @return $this - */ - public function setId($id) { - $this->id = $id; - - return $this; - } - - /** - * @return JobResult - */ - public function getResultBag() { - return $this->resultBag; - } - - /** - * @param JobResult $resultBag - * @return $this - */ - public function setResultBag(JobResult $resultBag) { - $this->resultBag = $resultBag; - - return $this; - } - - /** - * @return int - */ - public function getOwnerId() { - return $this->ownerId; - } - - /** - * @param int $ownerId - * @return $this - */ - public function setOwnerId($ownerId) { - $this->ownerId = $ownerId; - - return $this; - } +class Job implements IArrayable +{ + /** + * @var string + */ + public const STATE_FRESH = 'fresh'; + + /** + * @var string + */ + public const STATE_FINISHED = 'finished'; + + /** + * @var string + */ + public const STATE_FAILED = 'failed'; + + /** + * @var string + */ + public const STATE_IN_PROGRESS = 'in progress'; + + /** + * @var int + */ + protected $id = 0; + + /** + * @var array + */ + protected $payload; + + /** + * @var string + */ + protected $worker; + + /** + * @var string + */ + protected $state; + + /** + * @var JobResult + */ + protected $resultBag; + + /** + * @var int + */ + protected $ownerId; + + /** + * Job constructor + */ + public function __construct() + { + $this->state = static::STATE_FRESH; + $this->resultBag = new JobResult(); + } + + /** + * Converts instance to array. + * + * @return array + */ + public function toArray() + { + return [ + 'id' => $this->id, + 'title' => $this->worker, + 'payload' => $this->payload, + 'state' => $this->state, + 'result' => $this->resultBag->toArray(), + 'sys_owner' => $this->ownerId + ]; + } + + /** + * Returns all valid states. + * + * @return array + */ + public static function getValidStates() + { + return [ + static::STATE_FRESH, + static::STATE_FAILED, + static::STATE_FINISHED, + static::STATE_IN_PROGRESS + ]; + } + + /** + * Creates instance from array. + * + * @return static + */ + public static function fromArray(array $object) + { + $job = new static(); + $job->setId(getFromArray($object, 'id', 0)); + $workerClass = getFromArray($object, 'title'); + $state = getFromArray($object, 'state', static::STATE_FRESH); + + return $job->setState($state) + ->setPayload(getFromArray($object, 'payload', [])) + ->setWorkerClass($workerClass) + ->setOwnerId(getFromArray($object, 'sys_owner', 0)); + } + + /** + * @return $this + */ + public function setPayload(array $payload) + { + $this->payload = $payload; + + return $this; + } + + /** + * @return array + */ + public function getPayload() + { + return $this->payload; + } + + /** + * @return $this + */ + public function setWorker(WorkerInterface $worker) + { + $this->setWorkerClass($worker::class); + + return $this; + } + + /** + * @param string $worker + * + * @return $this + */ + public function setWorkerClass($worker) + { + $this->worker = $worker; + + return $this; + } + + /** + * @return WorkerInterface + */ + public function getWorker() + { + if (!class_exists($this->worker)) { + throw new UnexpectedValueException( + sprintf('Worker class does not exist: %s', $this->worker) + ); + } + + $worker = new $this->worker(); + if (!($worker instanceof WorkerInterface)) { + throw new UnexpectedValueException( + 'Worker class does not implement PP\Lib\PersistentQueue\WorkerInterface' + ); + } + + return $worker; + } + + /** + * @return string + */ + public function getState() + { + return $this->state; + } + + /** + * @param $state + * @return $this + */ + public function setState($state) + { + $validStates = static::getValidStates(); + if (!in_array($state, $validStates, true)) { + throw new UnexpectedValueException( + sprintf('State is invalid. Valid states: %s', join(', ', $validStates)) + ); + } + + $this->state = $state; + + return $this; + } + + /** + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * @param $id + * @return $this + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * @return JobResult + */ + public function getResultBag() + { + return $this->resultBag; + } + + /** + * @return $this + */ + public function setResultBag(JobResult $resultBag) + { + $this->resultBag = $resultBag; + + return $this; + } + + /** + * @return int + */ + public function getOwnerId() + { + return $this->ownerId; + } + + /** + * @param int $ownerId + * @return $this + */ + public function setOwnerId($ownerId) + { + $this->ownerId = $ownerId; + + return $this; + } } diff --git a/src/Lib/PersistentQueue/JobResult.php b/src/Lib/PersistentQueue/JobResult.php index bb728217..0b2da24d 100644 --- a/src/Lib/PersistentQueue/JobResult.php +++ b/src/Lib/PersistentQueue/JobResult.php @@ -2,7 +2,7 @@ namespace PP\Lib\PersistentQueue; -use \ArrayIterator; +use ArrayIterator; use PP\Lib\IArrayable; /** @@ -10,150 +10,159 @@ * * @package PP\Lib\PersistentQueue */ -class JobResult implements IArrayable { - - /** - * @var string - */ - public const RESULT_ERRORS = 'errors'; - - /** - * @var string - */ - public const RESULT_INFO = 'info'; - - /** - * @var string - */ - public const RESULT_NOTICES = 'notices'; - - /** - * @var ArrayIterator - */ - protected $errors; - - /** - * @var ArrayIterator - */ - protected $info; - - /** - * @var ArrayIterator - */ - protected $notices; - - /** - * JobResult constructor. - */ - public function __construct() { - $this->errors = new ArrayIterator(); - $this->info = new ArrayIterator(); - $this->notices = new ArrayIterator(); - } - - /** - * @param $message - * @return $this - */ - public function addError($message) { - $this->errors->append($message); - - return $this; - } - - /** - * @param $message - * @return $this - */ - public function addInfo($message) { - $this->info->append($message); - - return $this; - } - - /** - * @param $message - * @return $this - */ - public function addNotice($message) { - $this->notices->append($message); - - return $this; - } - - /** - * @param array $errors - * @return $this - */ - public function setErrors(array $errors) { - $this->errors = new ArrayIterator($errors); - - return $this; - } - - /** - * @param array $info - * @return $this - */ - public function setInfo(array $info) { - $this->info = new ArrayIterator($info); - - return $this; - } - - /** - * @param array $notices - * @return $this - */ - public function setNotices(array $notices) { - $this->notices = new ArrayIterator($notices); - - return $this; - } - - /** - * @inheritdoc - */ - public function toArray() { - return [ - static::RESULT_ERRORS => $this->errors->getArrayCopy(), - static::RESULT_INFO => $this->info->getArrayCopy(), - static::RESULT_NOTICES => $this->notices->getArrayCopy() - ]; - } - - /** - * @inheritdoc - */ - public static function fromArray(array $object) { - /** @var JobResult $instance */ - $instance = new static(); - - return $instance - ->setErrors(getFromArray($object, static::RESULT_ERRORS, [])) - ->setInfo(getFromArray($object, static::RESULT_INFO, [])) - ->setNotices(getFromArray($object, static::RESULT_NOTICES, [])); - } - - /** - * @return ArrayIterator - */ - public function getErrors() { - return $this->errors; - } - - /** - * @return ArrayIterator - */ - public function getInfo() { - return $this->info; - } - - /** - * @return ArrayIterator - */ - public function getNotices() { - return $this->notices; - } +class JobResult implements IArrayable +{ + /** + * @var string + */ + public const RESULT_ERRORS = 'errors'; + + /** + * @var string + */ + public const RESULT_INFO = 'info'; + + /** + * @var string + */ + public const RESULT_NOTICES = 'notices'; + + /** + * @var ArrayIterator + */ + protected $errors; + + /** + * @var ArrayIterator + */ + protected $info; + + /** + * @var ArrayIterator + */ + protected $notices; + + /** + * JobResult constructor. + */ + public function __construct() + { + $this->errors = new ArrayIterator(); + $this->info = new ArrayIterator(); + $this->notices = new ArrayIterator(); + } + + /** + * @param $message + * @return $this + */ + public function addError($message) + { + $this->errors->append($message); + + return $this; + } + + /** + * @param $message + * @return $this + */ + public function addInfo($message) + { + $this->info->append($message); + + return $this; + } + + /** + * @param $message + * @return $this + */ + public function addNotice($message) + { + $this->notices->append($message); + + return $this; + } + + /** + * @return $this + */ + public function setErrors(array $errors) + { + $this->errors = new ArrayIterator($errors); + + return $this; + } + + /** + * @return $this + */ + public function setInfo(array $info) + { + $this->info = new ArrayIterator($info); + + return $this; + } + + /** + * @return $this + */ + public function setNotices(array $notices) + { + $this->notices = new ArrayIterator($notices); + + return $this; + } + + /** + * @inheritdoc + */ + public function toArray() + { + return [ + static::RESULT_ERRORS => $this->errors->getArrayCopy(), + static::RESULT_INFO => $this->info->getArrayCopy(), + static::RESULT_NOTICES => $this->notices->getArrayCopy() + ]; + } + + /** + * @inheritdoc + */ + public static function fromArray(array $object) + { + /** @var JobResult $instance */ + $instance = new static(); + + return $instance + ->setErrors(getFromArray($object, static::RESULT_ERRORS, [])) + ->setInfo(getFromArray($object, static::RESULT_INFO, [])) + ->setNotices(getFromArray($object, static::RESULT_NOTICES, [])); + } + + /** + * @return ArrayIterator + */ + public function getErrors() + { + return $this->errors; + } + + /** + * @return ArrayIterator + */ + public function getInfo() + { + return $this->info; + } + + /** + * @return ArrayIterator + */ + public function getNotices() + { + return $this->notices; + } } diff --git a/src/Lib/PersistentQueue/Queue.php b/src/Lib/PersistentQueue/Queue.php index d227d01a..042a0811 100644 --- a/src/Lib/PersistentQueue/Queue.php +++ b/src/Lib/PersistentQueue/Queue.php @@ -11,109 +11,111 @@ * * @package PP\Lib\PersistentQueue */ -class Queue { - - /** - * @var string - */ - public const JOB_DB_TYPE = 'queue_job'; - - /** - * @var string - */ - public const JOB_FETCH_LIMIT = 1; - - /** - * @var PXApplication - */ - protected $app; - - /** - * @var PXDatabase - */ - protected $db; - - /** - * Queue constructor. - * - * @param PXApplication $app - * @param PXDatabase $db - * @throws Exception - */ - public function __construct(PXApplication $app, PXDatabase $db) { - $this->app = $app; - $this->db = $db; - if (!isset($this->app->types[static::JOB_DB_TYPE])) { - throw new Exception( - sprintf("Queue job fatal error: datatype `%s` missed in datatypes.xml", static::JOB_DB_TYPE)); - } - } - - /** - * @param Job $job - * @return int - */ - public function addJob(Job $job) { - $contentType = $this->app->types[static::JOB_DB_TYPE]; - $jobObject = $job->toArray(); - - $stub = $this->app->initContentObject(static::JOB_DB_TYPE); - $object = array_merge($stub, $jobObject); - - return $this->db->addContentObject($contentType, $object); - } - - /** - * @param Job $job - * @return null - */ - protected function updateJob(Job $job) { - $contentType = $this->app->types[static::JOB_DB_TYPE]; - return $this->db->modifyContentObject($contentType, $job->toArray()); - } - - /** - * @param Job $job - * @return null - */ - public function finishJob(Job $job) { - $job->setState(Job::STATE_FINISHED); - return $this->updateJob($job); - } - - /** - * @param Job $job - * @return null - */ - public function failJob(Job $job) { - $job->setState(Job::STATE_FAILED); - return $this->updateJob($job); - } - - /** - * @param Job $job - * @return null - */ - public function startJob(Job $job) { - $job->setState(Job::STATE_IN_PROGRESS); - return $this->updateJob($job); - } - - /** - * @param int $limit - * @return Job[] - */ - public function getFreshJobs($limit = self::JOB_FETCH_LIMIT) { - $contentType = $this->app->types[static::JOB_DB_TYPE]; - $objects = $this->db->getObjectsByFieldLimited( - $contentType, true, - 'state', Job::STATE_FRESH, - $limit, 0 - ); - - return array_map(function ($object) { - return Job::fromArray($object); - }, $objects); - } +class Queue +{ + /** + * @var string + */ + public const JOB_DB_TYPE = 'queue_job'; + + /** + * @var string + */ + public const JOB_FETCH_LIMIT = 1; + + /** + * @var PXApplication + */ + protected $app; + + /** + * @var PXDatabase + */ + protected $db; + + /** + * Queue constructor. + * + * @throws Exception + */ + public function __construct(PXApplication $app, PXDatabase $db) + { + $this->app = $app; + $this->db = $db; + if (!$this->app->getDataType(static::JOB_DB_TYPE)) { + throw new Exception( + sprintf("Queue job fatal error: datatype `%s` missed in datatypes.xml", static::JOB_DB_TYPE) + ); + } + } + + /** + * @return int + */ + public function addJob(Job $job) + { + $contentType = $this->app->getDataType(static::JOB_DB_TYPE); + $jobObject = $job->toArray(); + + $stub = $this->app->initContentObject(static::JOB_DB_TYPE); + $object = array_merge($stub, $jobObject); + + return $this->db->addContentObject($contentType, $object); + } + + /** + * @return null + */ + protected function updateJob(Job $job) + { + $contentType = $this->app->getDataType(static::JOB_DB_TYPE); + return $this->db->modifyContentObject($contentType, $job->toArray()); + } + + /** + * @return null + */ + public function finishJob(Job $job) + { + $job->setState(Job::STATE_FINISHED); + return $this->updateJob($job); + } + + /** + * @return null + */ + public function failJob(Job $job) + { + $job->setState(Job::STATE_FAILED); + return $this->updateJob($job); + } + + /** + * @return null + */ + public function startJob(Job $job) + { + $job->setState(Job::STATE_IN_PROGRESS); + return $this->updateJob($job); + } + + /** + * @param int $limit + * @return Job[] + */ + public function getFreshJobs($limit = self::JOB_FETCH_LIMIT) + { + $contentType = $this->app->getDataType(static::JOB_DB_TYPE); + $objects = $this->db->getObjectsByFieldLimited( + $contentType, + true, + 'state', + Job::STATE_FRESH, + $limit, + 0 + ); + + return array_map(fn ($object) => Job::fromArray($object), $objects); + } } diff --git a/src/Lib/PersistentQueue/WorkerInterface.php b/src/Lib/PersistentQueue/WorkerInterface.php index 6564f8da..2291766f 100644 --- a/src/Lib/PersistentQueue/WorkerInterface.php +++ b/src/Lib/PersistentQueue/WorkerInterface.php @@ -7,18 +7,16 @@ * * @package PP\Lib\PersistentQueue */ -interface WorkerInterface { +interface WorkerInterface +{ + /** + * @return mixed + */ + public function run(array $payload = []); - /** - * @param array $payload - * @return mixed - */ - public function run(array $payload = []); - - /** - * @param Job $job - * @return $this - */ - public function setJob(Job $job); + /** + * @return $this + */ + public function setJob(Job $job); } diff --git a/src/Lib/Rss/AbstractRssNode.php b/src/Lib/Rss/AbstractRssNode.php index e27dee9a..1c8ef921 100644 --- a/src/Lib/Rss/AbstractRssNode.php +++ b/src/Lib/Rss/AbstractRssNode.php @@ -9,33 +9,32 @@ */ abstract class AbstractRssNode { - - public function _node($nodeName, $value) - { - if (is_array($value)) { - $value = implode('', $value); - } - - return - <<$value XML; - } + } - public function nodeSet($nodes) - { - $_ = []; + public function nodeSet($nodes) + { + $_ = []; - foreach ($nodes as $node) { - if (method_exists($this, $node)) { - $_[] = $this->$node(); + foreach ($nodes as $node) { + if (method_exists($this, $node)) { + $_[] = $this->$node(); - } elseif (isset($this->_data[$node])) { - $_[] = $this->_node($node, $this->_data[$node]); - } - } + } elseif (isset($this->_data[$node])) { + $_[] = $this->_node($node, $this->_data[$node]); + } + } - return implode("\n", $_); - } + return implode("\n", $_); + } } diff --git a/src/Lib/Rss/RssChannel.php b/src/Lib/Rss/RssChannel.php index a15b7a96..d9db1807 100644 --- a/src/Lib/Rss/RssChannel.php +++ b/src/Lib/Rss/RssChannel.php @@ -9,49 +9,48 @@ */ class RssChannel extends AbstractRssNode { - - public function __construct($channel, &$rssEngine) - { - $this->_data = $channel; - $this->rss =& $rssEngine; - - $this->nodeNames = [ - 'title', 'link', 'description', 'language', 'copyright', - 'managingEditor', 'webMaster', 'generator', 'ttl', 'image', 'lastBuildDate', - ]; - } - - public function link() - { - return $this->_node('link', $this->_data['link'] . '?from=rss'); - } - - public function image() - { - $_ = [ - $this->_node('url', $this->_data['image']), - $this->_node('title', $this->_data['title']), - $this->link(), - ]; - - return $this->_node('image', $_); - } - - public function xml($items) - { - $_ = [ - $this->nodeSet($this->nodeNames), - ]; - - foreach ($items as $i) { - $i['pubDate'] = $this->rss->rssDateFormat($i['pubDate']); - - $itemO = new RssItem($i); - $_[] = $itemO->xml(); - } - - - return $this->_node('channel', $_); - } + public function __construct($channel, &$rssEngine) + { + $this->_data = $channel; + $this->rss = & $rssEngine; + + $this->nodeNames = [ + 'title', 'link', 'description', 'language', 'copyright', + 'managingEditor', 'webMaster', 'generator', 'ttl', 'image', 'lastBuildDate', + ]; + } + + public function link() + { + return $this->_node('link', $this->_data['link'] . '?from=rss'); + } + + public function image() + { + $_ = [ + $this->_node('url', $this->_data['image']), + $this->_node('title', $this->_data['title']), + $this->link(), + ]; + + return $this->_node('image', $_); + } + + public function xml($items) + { + $_ = [ + $this->nodeSet($this->nodeNames), + ]; + + foreach ($items as $i) { + $i['pubDate'] = $this->rss->rssDateFormat($i['pubDate']); + + $itemO = new RssItem($i); + $_[] = $itemO->xml(); + } + + + return $this->_node('channel', $_); + } } diff --git a/src/Lib/Rss/RssItem.php b/src/Lib/Rss/RssItem.php index 7cf4394d..5f3002b7 100644 --- a/src/Lib/Rss/RssItem.php +++ b/src/Lib/Rss/RssItem.php @@ -9,7 +9,6 @@ */ class RssItem extends AbstractRssNode { - public function __construct($item) { $this->_data = $item; diff --git a/src/Lib/Session/DatabaseHandler.php b/src/Lib/Session/DatabaseHandler.php index b3e892b7..0faa57b6 100644 --- a/src/Lib/Session/DatabaseHandler.php +++ b/src/Lib/Session/DatabaseHandler.php @@ -6,135 +6,143 @@ use PXDatabase; use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; -class DatabaseHandler extends NullSessionHandler { - - public const SESSION_TABLE = 'admin_session'; - - /** @var PXDatabase|PostgreSqlDriver */ - protected $db; - protected $gcCalled; - - public function __construct(PXDatabase $db) { - $this->db = $db; - } - - /** - * @param string $savePath - * @param string $sessionName - * @return bool - */ - public function open($savePath, $sessionName) { - return true; - } - - /** - * @return bool - */ - public function close() { - if ($this->gcCalled) { - $sql = sprintf( - "DELETE FROM %s WHERE (session_lifetime + session_time) < %d", - static::SESSION_TABLE, - time() - ); - - $this->db->ModifyingQuery($sql, null, null, false); - } - - return true; - } - - /** - * @param string $sessionId - * @return bool - */ - public function read($sessionId) { - $sessionId = $this->db->EscapeString($sessionId); - - $sql = sprintf( - "SELECT * FROM %s WHERE session_id = '%s'", - static::SESSION_TABLE, - $sessionId - ); - - $row = $this->db->Query($sql, true); - if (($row = reset($row)) !== false) { - $maxTime = (int)$row['session_time'] + (int)$row['session_lifetime']; - if ($maxTime > time()) { - return json_decode($row['session_data']); - } - } - - return ''; - } - - /** - * @param string $sessionId - * @param string $data - * @return bool - */ - public function write($sessionId, $data) { - $sessionId = $this->db->EscapeString($sessionId); - $data = $this->db->EscapeString(json_encode($data)); - $maxlifetime = (int)ini_get('session.gc_maxlifetime'); - $now = time(); - - $sql = sprintf( - "UPDATE %s +class DatabaseHandler extends NullSessionHandler +{ + public const SESSION_TABLE = 'admin_session'; + + /** @var PXDatabase|PostgreSqlDriver */ + protected $db; + protected $gcCalled; + + public function __construct(PXDatabase $db) + { + $this->db = $db; + } + + /** + * @param string $savePath + * @param string $sessionName + * @return bool + */ + public function open($savePath, $sessionName): bool + { + return true; + } + + /** + * @return bool + */ + public function close(): bool + { + if ($this->gcCalled) { + $sql = sprintf( + "DELETE FROM %s WHERE (session_lifetime + session_time) < %d", + static::SESSION_TABLE, + time() + ); + + $this->db->ModifyingQuery($sql, null, null, false); + } + + return true; + } + + /** + * @param string $sessionId + * @return string + * @throws \JsonException + */ + public function read($sessionId): string + { + $sessionId = $this->db->EscapeString($sessionId); + + $sql = sprintf( + "SELECT * FROM %s WHERE session_id = '%s'", + static::SESSION_TABLE, + $sessionId + ); + + $row = $this->db->Query($sql, true); + if (($row = reset($row)) !== false) { + $maxTime = (int)$row['session_time'] + (int)$row['session_lifetime']; + if ($maxTime > time()) { + return json_decode((string) $row['session_data']); + } + } + + return ''; + } + + /** + * @param string $sessionId + * @param string $data + * @return bool + */ + public function write($sessionId, $data): bool + { + $sessionId = $this->db->EscapeString($sessionId); + $data = $this->db->EscapeString(json_encode($data)); + $maxlifetime = (int)ini_get('session.gc_maxlifetime'); + $now = time(); + + $sql = sprintf( + "UPDATE %s SET session_data = '%s', session_lifetime = %d, session_time = %d WHERE session_id = '%s'", - static::SESSION_TABLE, - $data, - $maxlifetime, - $now, - $sessionId - ); - - $result = $this->db->ModifyingQuery($sql, null, null, false, true); - if ($result === 0) { - $sql = sprintf( - "INSERT INTO %s + static::SESSION_TABLE, + $data, + $maxlifetime, + $now, + $sessionId + ); + + $result = $this->db->ModifyingQuery($sql, null, null, false, true); + if ($result === 0) { + $sql = sprintf( + "INSERT INTO %s (session_id, session_data, session_lifetime, session_time) VALUES ('%s', '%s', %d, %d)", - static::SESSION_TABLE, - $sessionId, - $data, - $maxlifetime, - $now - ); - - $this->db->ModifyingQuery($sql, null, null, false, false); - } - - return true; - } - - /** - * Destroy session - * @param string $sessionId - * @return bool - */ - public function destroy($sessionId) { - $sessionId = $this->db->EscapeString($sessionId); - $sql = sprintf( - "DELETE FROM %s WHERE session_id = '%s'", - static::SESSION_TABLE, - $sessionId - ); - - $this->db->ModifyingQuery($sql, null, null, false); - - return true; - } - - /** - * @param int $maxlifetime - * @return bool - */ - public function gc($maxlifetime) { - $this->gcCalled = true; - - return true; - } + static::SESSION_TABLE, + $sessionId, + $data, + $maxlifetime, + $now + ); + + $this->db->ModifyingQuery($sql, null, null, false, false); + } + + return true; + } + + /** + * Destroy session + * @param string $sessionId + * @return bool + */ + public function destroy($sessionId): bool + { + $sessionId = $this->db->EscapeString($sessionId); + $sql = sprintf( + "DELETE FROM %s WHERE session_id = '%s'", + static::SESSION_TABLE, + $sessionId + ); + + $this->db->ModifyingQuery($sql, null, null, false); + + return true; + } + + /** + * @param int $maxlifetime + * @return int|bool + */ + public function gc($maxlifetime): int|false + { + $this->gcCalled = true; + + return true; + } } diff --git a/src/Lib/UrlGenerator/AbstractUrlGenerator.php b/src/Lib/UrlGenerator/AbstractUrlGenerator.php index 0e9f5c20..5b79a612 100644 --- a/src/Lib/UrlGenerator/AbstractUrlGenerator.php +++ b/src/Lib/UrlGenerator/AbstractUrlGenerator.php @@ -8,83 +8,78 @@ * Class AbstractUrlGenerator * @package PP\Lib\UrlGenerator */ -abstract class AbstractUrlGenerator implements GeneratorInterface { +abstract class AbstractUrlGenerator implements GeneratorInterface +{ + /** @var ContextUrlGenerator */ + protected $context; - /** @var ContextUrlGenerator */ - protected $context; + /** + * AbstractUrlGenerator constructor. + */ + public function __construct(ContextUrlGenerator $context) + { + $this->context = $context; + } - /** - * AbstractUrlGenerator constructor. - * @param ContextUrlGenerator $context - */ - public function __construct(ContextUrlGenerator $context) { - $this->context = $context; - } + /** + * {@inheritDoc} + */ + public function generate($params = []) + { + $action = $this->context->getTargetAction(); + return match ($action) { + ModuleInterface::ACTION_INDEX => $this->indexUrl($params), + ModuleInterface::ACTION_ACTION => $this->actionUrl($params), + ModuleInterface::ACTION_JSON => $this->jsonUrl($params), + ModuleInterface::ACTION_POPUP => $this->popupUrl($params), + default => throw new \LogicException("Action '$action' doesn't exist."), + }; + } - /** - * {@inheritDoc} - */ - public function generate($params = []) { - $action = $this->context->getTargetAction(); - switch ($action) { - case ModuleInterface::ACTION_INDEX: - $url = $this->indexUrl($params); - break; - case ModuleInterface::ACTION_ACTION: - $url = $this->actionUrl($params); - break; - case ModuleInterface::ACTION_JSON: - $url = $this->jsonUrl($params); - break; - case ModuleInterface::ACTION_POPUP: - $url = $this->popupUrl($params); - break; - default: - throw new \LogicException("Action '$action' doesn't exist."); - } - return $url; - } + /** + * @return ContextUrlGenerator + */ + public function getContext() + { + return $this->context; + } - /** - * @return ContextUrlGenerator - */ - public function getContext() { - return $this->context; - } + /** + * @return string + * @throws \LogicException + */ + protected function getArea() + { + if (!$this->context->hasCurrentModule() && !$this->context->hasTargetModule()) { + throw new \LogicException('Don\'t given target module and current module.'); + } + $area = $this->context->getTargetModule(); + if ($area === null) { + $area = $this->context->getCurrentModule(); + } + return $area; + } - /** - * @return string - * @throws \LogicException - */ - protected function getArea() { - if (!$this->context->hasCurrentModule() && !$this->context->hasTargetModule()) { - throw new \LogicException('Don\'t given target module and current module.'); - } - $area = $this->context->getTargetModule(); - if ($area === null) { - $area = $this->context->getCurrentModule(); - } - return $area; - } + /** + * @param string $url + * @param array [string]string $params + * @return string + */ + protected function generateUrl($url, $params = []) + { + $queryString = http_build_query($params); + return "$url?$queryString"; + } - /** - * @param string $url - * @param array [string]string $params - * @return string - */ - protected function generateUrl($url, $params = []) { - $queryString = http_build_query($params); - return "$url?$queryString"; - } - - /** - * @return string|null - */ - protected function getSid() { - $sid = null; - if ($this->context->hasRequest()) { - $sid = $this->context->getRequest()->getSid(); - } - return $sid; - } + /** + * @return string|null + */ + protected function getSid() + { + $sid = null; + if ($this->context->hasRequest()) { + $sid = $this->context->getRequest()->getSid(); + } + return $sid; + } } diff --git a/src/Lib/UrlGenerator/AdminUrlGenerator.php b/src/Lib/UrlGenerator/AdminUrlGenerator.php index 8b9a3941..7f066f28 100644 --- a/src/Lib/UrlGenerator/AdminUrlGenerator.php +++ b/src/Lib/UrlGenerator/AdminUrlGenerator.php @@ -6,54 +6,62 @@ * Class AdminUrlGenerator * @package PP\Lib\UrlGenerator */ -class AdminUrlGenerator extends AbstractUrlGenerator { +class AdminUrlGenerator extends AbstractUrlGenerator +{ + /** + * {@inheritDoc} + */ + public function indexUrl($params = []) + { + $oldParams = []; + $url = '/admin/'; + $oldParams['area'] = $this->getArea(); + $params = array_replace($oldParams, $params); + return $this->generateUrl($url, $params); + } - /** - * {@inheritDoc} - */ - public function indexUrl($params = []) { - $url = '/admin/'; - $oldParams['area'] = $this->getArea(); - $params = array_replace($oldParams, $params); - return $this->generateUrl($url, $params); - } + /** + * {@inheritDoc} + */ + public function actionUrl($params = []) + { + $oldParams = []; + $url = '/admin/action.phtml'; + $oldParams['area'] = $this->getArea(); + $sid = $this->getSid(); + if ($sid !== null) { + $oldParams['sid'] = $sid; + } + $params = array_replace($oldParams, $params); + return $this->generateUrl($url, $params); + } - /** - * {@inheritDoc} - */ - public function actionUrl($params = []) { - $url = '/admin/action.phtml'; - $oldParams['area'] = $this->getArea(); - $sid = $this->getSid(); - if ($sid !== null) { - $oldParams['sid'] = $sid; - } - $params = array_replace($oldParams, $params); - return $this->generateUrl($url, $params); - } + /** + * {@inheritDoc} + */ + public function jsonUrl($params = []) + { + $oldParams = []; + $url = '/admin/json.phtml'; + $oldParams['area'] = $this->getArea(); + $params = array_replace($oldParams, $params); + return $this->generateUrl($url, $params); + } - /** - * {@inheritDoc} - */ - public function jsonUrl($params = []) { - $url = '/admin/json.phtml'; - $oldParams['area'] = $this->getArea(); - $params = array_replace($oldParams, $params); - return $this->generateUrl($url, $params); - } - - /** - * {@inheritDoc} - */ - public function popupUrl($params = []) { - $url = '/admin/popup.phtml'; - $oldParams['area'] = $this->getArea(); - $sid = $this->getSid(); - if ($sid !== null) { - $oldParams['sid'] = $sid; - } - $params = array_replace($oldParams, $params); - return $this->generateUrl($url, $params); - } + /** + * {@inheritDoc} + */ + public function popupUrl($params = []) + { + $oldParams = []; + $url = '/admin/popup.phtml'; + $oldParams['area'] = $this->getArea(); + $sid = $this->getSid(); + if ($sid !== null) { + $oldParams['sid'] = $sid; + } + $params = array_replace($oldParams, $params); + return $this->generateUrl($url, $params); + } } diff --git a/src/Lib/UrlGenerator/ContextUrlGenerator.php b/src/Lib/UrlGenerator/ContextUrlGenerator.php index f0cdd038..334047c5 100644 --- a/src/Lib/UrlGenerator/ContextUrlGenerator.php +++ b/src/Lib/UrlGenerator/ContextUrlGenerator.php @@ -6,103 +6,113 @@ * Class ContextUrlGenerator * @package PP\Lib\UrlGenerator */ -class ContextUrlGenerator { - - /** @var null|string */ - protected $targetAction; - - /** @var null|\PXRequest */ - protected $request; - - /** @var null|string */ - protected $targetModule; - - /** @var null|string */ - protected $currentModule; - - /** - * @return null|string - */ - public function getTargetAction() { - return $this->targetAction; - } - - /** - * @param string $targetAction - * @return $this - */ - public function setTargetAction($targetAction) { - $this->targetAction = $targetAction; - return $this; - } - - /** - * @return bool - */ - public function hasRequest() { - return isset($this->request); - } - - /** - * @return null|\PXRequest - */ - public function getRequest() { - return $this->request; - } - - /** - * @param \PXRequest $request - * @return $this - */ - public function setRequest(\PXRequest $request) { - $this->request = $request; - return $this; - } - - /** - * @return bool - */ - public function hasTargetModule() { - return isset($this->targetModule); - } - - /** - * @return null|string - */ - public function getTargetModule() { - return $this->targetModule; - } - - /** - * @param null|string $targetModule - * @return $this - */ - public function setTargetModule($targetModule) { - $this->targetModule = $targetModule; - return $this; - } - - /** - * @return bool - */ - public function hasCurrentModule() { - return isset($this->currentModule); - } - - /** - * @return null|string - */ - public function getCurrentModule() { - return $this->currentModule; - } - - /** - * @param string $currentModule - * @return $this - */ - public function setCurrentModule($currentModule) { - $this->currentModule = $currentModule; - return $this; - } +class ContextUrlGenerator +{ + /** @var null|string */ + protected $targetAction; + + /** @var null|\PXRequest */ + protected $request; + + /** @var null|string */ + protected $targetModule; + + /** @var null|string */ + protected $currentModule; + + /** + * @return null|string + */ + public function getTargetAction() + { + return $this->targetAction; + } + + /** + * @param string $targetAction + * @return $this + */ + public function setTargetAction($targetAction) + { + $this->targetAction = $targetAction; + return $this; + } + + /** + * @return bool + */ + public function hasRequest() + { + return isset($this->request); + } + + /** + * @return null|\PXRequest + */ + public function getRequest() + { + return $this->request; + } + + /** + * @return $this + */ + public function setRequest(\PXRequest $request) + { + $this->request = $request; + return $this; + } + + /** + * @return bool + */ + public function hasTargetModule() + { + return isset($this->targetModule); + } + + /** + * @return null|string + */ + public function getTargetModule() + { + return $this->targetModule; + } + + /** + * @param null|string $targetModule + * @return $this + */ + public function setTargetModule($targetModule) + { + $this->targetModule = $targetModule; + return $this; + } + + /** + * @return bool + */ + public function hasCurrentModule() + { + return isset($this->currentModule); + } + + /** + * @return null|string + */ + public function getCurrentModule() + { + return $this->currentModule; + } + + /** + * @param string $currentModule + * @return $this + */ + public function setCurrentModule($currentModule) + { + $this->currentModule = $currentModule; + return $this; + } } diff --git a/src/Lib/UrlGenerator/GeneratorInterface.php b/src/Lib/UrlGenerator/GeneratorInterface.php index 4313697b..c8f1bb1c 100644 --- a/src/Lib/UrlGenerator/GeneratorInterface.php +++ b/src/Lib/UrlGenerator/GeneratorInterface.php @@ -6,36 +6,36 @@ * Interface GeneratorInterface * @package PP\Lib\UrlGenerator */ -interface GeneratorInterface { - - /** - * @param array[string]string $params - * @return string - */ - public function generate($params = []); - - /** - * @param array[string]string $params - * @return string - */ - public function indexUrl($params = []); - - /** - * @param array[string]string $params - * @return string - */ - public function actionUrl($params = []); - - /** - * @param array[string]string $params - * @return string - */ - public function jsonUrl($params = []); - - /** - * @param array[string]string $params - * @return string - */ - public function popupUrl($params = []); +interface GeneratorInterface +{ + /** + * @param array[string]string $params + * @return string + */ + public function generate($params = []); + + /** + * @param array[string]string $params + * @return string + */ + public function indexUrl($params = []); + + /** + * @param array[string]string $params + * @return string + */ + public function actionUrl($params = []); + + /** + * @param array[string]string $params + * @return string + */ + public function jsonUrl($params = []); + + /** + * @param array[string]string $params + * @return string + */ + public function popupUrl($params = []); } diff --git a/src/Lib/UrlGenerator/UrlGenerator.php b/src/Lib/UrlGenerator/UrlGenerator.php index d21c2e85..2302ebfe 100644 --- a/src/Lib/UrlGenerator/UrlGenerator.php +++ b/src/Lib/UrlGenerator/UrlGenerator.php @@ -6,63 +6,67 @@ * Class UrlGenerator * @package PP\Lib\UrlGenerator */ -class UrlGenerator { +class UrlGenerator +{ + /** @var ContextUrlGenerator */ + protected $context; - /** @var ContextUrlGenerator */ - protected $context; + /** @var AdminUrlGenerator */ + protected $adminGeneratorInstance; - /** @var AdminUrlGenerator */ - protected $adminGeneratorInstance; + /** @var UserUrlGenerator */ + protected $userGeneratorInstance; - /** @var UserUrlGenerator */ - protected $userGeneratorInstance; + /** + * UrlGenerator constructor. + */ + public function __construct(ContextUrlGenerator $context) + { + $this->context = $context; + } - /** - * UrlGenerator constructor. - * @param ContextUrlGenerator $context - */ - public function __construct(ContextUrlGenerator $context) { - $this->context = $context; - } + /** + * @return GeneratorInterface + */ + public function getUserGenerator() + { + if ($this->userGeneratorInstance === null) { + $this->userGeneratorInstance = new UserUrlGenerator($this->context); + } + return $this->userGeneratorInstance; + } - /** - * @return GeneratorInterface - */ - public function getUserGenerator() { - if ($this->userGeneratorInstance === null) { - $this->userGeneratorInstance = new UserUrlGenerator($this->context); - } - return $this->userGeneratorInstance; - } + /** + * @return GeneratorInterface + */ + public function getAdminGenerator() + { + if ($this->adminGeneratorInstance === null) { + $this->adminGeneratorInstance = new AdminUrlGenerator($this->context); + } + return $this->adminGeneratorInstance; + } - /** - * @return GeneratorInterface - */ - public function getAdminGenerator() { - if ($this->adminGeneratorInstance === null) { - $this->adminGeneratorInstance = new AdminUrlGenerator($this->context); - } - return $this->adminGeneratorInstance; - } + /** + * @return ContextUrlGenerator + */ + public function getContext() + { + return $this->context; + } - /** - * @return ContextUrlGenerator - */ - public function getContext() { - return $this->context; - } - - /** - * @param ContextUrlGenerator $context - * @return $this - */ - public function setContext($context) { - if ($this->context !== $context) { - $this->adminGeneratorInstance = null; - $this->userGeneratorInstance = null; - } - $this->context = $context; - return $this; - } + /** + * @param ContextUrlGenerator $context + * @return $this + */ + public function setContext($context) + { + if ($this->context !== $context) { + $this->adminGeneratorInstance = null; + $this->userGeneratorInstance = null; + } + $this->context = $context; + return $this; + } } diff --git a/src/Lib/UrlGenerator/UserUrlGenerator.php b/src/Lib/UrlGenerator/UserUrlGenerator.php index 9f91d962..e6ae36c3 100644 --- a/src/Lib/UrlGenerator/UserUrlGenerator.php +++ b/src/Lib/UrlGenerator/UserUrlGenerator.php @@ -6,35 +6,39 @@ * Class UserUrlGenerator * @package PP\Lib\UrlGenerator */ -class UserUrlGenerator extends AbstractUrlGenerator { +class UserUrlGenerator extends AbstractUrlGenerator +{ + /** + * {@inheritDoc} + */ + public function indexUrl($params = []): void + { + throw new \LogicException('You cannot use the method: ' . __METHOD__); + } - /** - * {@inheritDoc} - */ - public function indexUrl($params = []) { - throw new \LogicException('You cannot use the method: ' . __METHOD__); - } + /** + * {@inheritDoc} + */ + public function actionUrl($params = []) + { + $url = '/' . $this->getArea() . '.action'; + return $this->generateUrl($url, $params); + } - /** - * {@inheritDoc} - */ - public function actionUrl($params = []) { - $url = '/' . $this->getArea() . '.action'; - return $this->generateUrl($url, $params); - } + /** + * {@inheritDoc} + */ + public function jsonUrl($params = []) + { + $url = '/' . $this->getArea() . '.json'; + return $this->generateUrl($url, $params); + } - /** - * {@inheritDoc} - */ - public function jsonUrl($params = []) { - $url = '/' . $this->getArea() . '.json'; - return $this->generateUrl($url, $params); - } - - /** - * {@inheritDoc} - */ - public function popupUrl($params = []) { - throw new \LogicException('You cannot use the method: ' . __METHOD__); - } + /** + * {@inheritDoc} + */ + public function popupUrl($params = []): void + { + throw new \LogicException('You cannot use the method: ' . __METHOD__); + } } diff --git a/src/Lib/Xml/AbstractXml.php b/src/Lib/Xml/AbstractXml.php index e2480ef6..cb71f72b 100644 --- a/src/Lib/Xml/AbstractXml.php +++ b/src/Lib/Xml/AbstractXml.php @@ -8,46 +8,33 @@ */ abstract class AbstractXml implements XmlInterface { - - public $xmlObject; - public $errors; - - /** - * @param string|object $testingObject - * @param int $nodeType - * @param string $fileLoader - * @param string $stringLoader - * @return bool|SimpleXml - */ - public function identEntity($testingObject, $nodeType, $fileLoader, $stringLoader) - { - set_error_handler(['PP\Lib\Xml\XmlErrors', 'addError'], E_ALL); - - switch (true) { - case is_a($testingObject, $nodeType): - $xmlObject = $testingObject; - break; - - case is_callable($fileLoader) && (file_exists($testingObject) || mb_strlen(getFromArray(parse_url($testingObject), 'scheme'))): - $xmlObject = @$fileLoader($testingObject); - break; - - case is_callable($stringLoader) && is_string($testingObject): - $xmlObject = @$stringLoader($testingObject); - break; - - default: - $xmlObject = false; - break; - } - - restore_error_handler(); - - return $xmlObject; - } - - /** - * {@inheritdoc} - */ - abstract public function xpath($query); + public $xmlObject; + public $errors; + + /** + * @param string|object $testingObject + * @param int $nodeType + * @param string $fileLoader + * @param string $stringLoader + */ + public function identEntity($testingObject, $nodeType, $fileLoader, $stringLoader): bool|\SimpleXMLElement + { + set_error_handler(\PP\Lib\Xml\XmlErrors::addError(...), E_ALL); + + $xmlObject = match (true) { + is_a($testingObject, $nodeType) => $testingObject, + is_callable($fileLoader) && (file_exists($testingObject) || mb_strlen((string) getFromArray(parse_url($testingObject), 'scheme'))) => @$fileLoader($testingObject), + is_callable($stringLoader) && is_string($testingObject) => @$stringLoader($testingObject), + default => false, + }; + + restore_error_handler(); + + return $xmlObject; + } + + /** + * {@inheritdoc} + */ + abstract public function xpath($query); } diff --git a/src/Lib/Xml/AbstractXmlNode.php b/src/Lib/Xml/AbstractXmlNode.php index 05bf3ada..92bbd1d5 100644 --- a/src/Lib/Xml/AbstractXmlNode.php +++ b/src/Lib/Xml/AbstractXmlNode.php @@ -6,153 +6,165 @@ * Class AbstractXmlNode * @package PP\Lib\Xml */ -abstract class AbstractXmlNode implements XmlNodeInterface { - /** @var XmlNodeInterface */ - protected $_xml; - protected $_node; - - protected $_nodeName; - protected $_nodeValue; - protected $_nodeType; - protected $_attributes; - protected $_childNodes; - - /** - * AbstractXmlNode constructor. - * @param $node - */ - public function __construct($node) { - $this->_node = $node; - } - - /** - * @return mixed - */ - abstract protected function _createXmlContext(); - - /** - * {@inheritdoc} - */ - public function xpath($query) { - $this->_createXmlContext(); - return $this->_xml->xpath($query); - } - - /** - * {@inheritdoc} - */ - public function nodeName() { - } - - /** - * {@inheritdoc} - */ - public function nodeValue() { - } - - /** - * {@inheritdoc} - */ - public function nodeType() { - } - - /** - * {@inheritdoc} - */ - public function attributes() { - } - - /** - * {@inheritdoc} - */ - public function isXmlNode() { - return $this->nodeType() == XML_ELEMENT_NODE; - } - - /** - * {@inheritdoc} - */ - public function childNodes() { - if (isset($this->_childNodes)) { - return $this->_childNodes; - } - - $this->_childNodes = []; - - if (is_object($this->_node)) { - $self = get_class($this); - $childs = $this->getChildObjects(); - - if ($childs) { - foreach ($childs as $node) { - $this->_childNodes[] = new $self($node); - } - } - } - - return $this->_childNodes; - } - - /** - * {@inheritdoc} - */ - public function getChildObjects() { - return []; - } - - /** - * @param $nodeName - * @return array|mixed|null - */ - public function getChildNode($nodeName) { - $find = []; - - foreach ($this->childNodes() as $node) { - if ($node->nodeName() == $nodeName) { - $find[] = $node; - } - } - - if (!sizeof($find)) { - return null; - } - - return sizeof($find) == 1 ? $find[0] : $find; - } - - /** - * @param $name - * @return array|bool|mixed|null - */ - public function __get($name) { - $attr = $this->getAttribute($name); - - if ($attr !== false) { - return $attr; - } - - return $this->getChildNode($name); - - } - - /** - * {@inheritdoc} - */ - public function getAttribute($attrName) { - $this->attributes(); - - if (count($this->_attributes) > 0 && isset($this->_attributes[$attrName])) { - return $this->_attributes[$attrName]->value; - } - - return false; - } - - /** - * {@inheritdoc} - */ - public function parent($selector = null) { - $ret = $this->xpath('..'); //TODO: make here selector context apply - return reset($ret); - } +abstract class AbstractXmlNode implements XmlNodeInterface +{ + /** @var XmlNodeInterface */ + protected $_xml; + + protected $_nodeName; + protected $_nodeValue; + protected $_nodeType; + protected $_attributes; + protected $_childNodes; + + /** + * AbstractXmlNode constructor. + * @param $node + */ + public function __construct(protected $_node) + { + } + + /** + * @return mixed + */ + abstract protected function _createXmlContext(); + + /** + * {@inheritdoc} + */ + public function xpath($query) + { + $this->_createXmlContext(); + return $this->_xml->xpath($query); + } + + /** + * {@inheritdoc} + */ + public function nodeName() + { + } + + /** + * {@inheritdoc} + */ + public function nodeValue() + { + } + + /** + * {@inheritdoc} + */ + public function nodeType() + { + } + + /** + * {@inheritdoc} + */ + public function attributes() + { + } + + /** + * {@inheritdoc} + */ + public function isXmlNode() + { + return $this->nodeType() == XML_ELEMENT_NODE; + } + + /** + * {@inheritdoc} + */ + public function childNodes() + { + if (isset($this->_childNodes)) { + return $this->_childNodes; + } + + $this->_childNodes = []; + + if (is_object($this->_node)) { + $self = static::class; + $childs = $this->getChildObjects(); + + if ($childs) { + foreach ($childs as $node) { + $this->_childNodes[] = new $self($node); + } + } + } + + return $this->_childNodes; + } + + /** + * {@inheritdoc} + */ + public function getChildObjects() + { + return []; + } + + /** + * @param $nodeName + * @return array|mixed|null + */ + public function getChildNode($nodeName) + { + $find = []; + + foreach ($this->childNodes() as $node) { + if ($node->nodeName() == $nodeName) { + $find[] = $node; + } + } + + if (!sizeof($find)) { + return null; + } + + return sizeof($find) == 1 ? $find[0] : $find; + } + + /** + * @param $name + * @return array|bool|mixed|null + */ + public function __get($name) + { + $attr = $this->getAttribute($name); + + if ($attr !== false) { + return $attr; + } + + return $this->getChildNode($name); + + } + + /** + * {@inheritdoc} + */ + public function getAttribute($attrName) + { + $this->attributes(); + + if ((is_countable($this->_attributes) ? count($this->_attributes) : 0) > 0 && isset($this->_attributes[$attrName])) { + return $this->_attributes[$attrName]->value; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function parent($selector = null) + { + $ret = $this->xpath('..'); //TODO: make here selector context apply + return reset($ret); + } } diff --git a/src/Lib/Xml/SimpleXml.php b/src/Lib/Xml/SimpleXml.php index 35af295f..eab74f1a 100644 --- a/src/Lib/Xml/SimpleXml.php +++ b/src/Lib/Xml/SimpleXml.php @@ -6,37 +6,39 @@ * Class SimpleXml * @package PP\Lib\Xml */ -class SimpleXml extends AbstractXml { +class SimpleXml extends AbstractXml +{ + /** + * SimpleXml constructor. + * @param $xmlEntity + */ + public function __construct($xmlEntity) + { + $this->xmlObject = $this->identEntity( + $xmlEntity, + 'SimpleXMLElement', + 'simplexml_load_file', + 'simplexml_load_string' + ); + } - /** - * SimpleXml constructor. - * @param $xmlEntity - */ - public function __construct($xmlEntity) { - $this->xmlObject = $this->identEntity( - $xmlEntity, - 'SimpleXMLElement', - 'simplexml_load_file', - 'simplexml_load_string' - ); - } + /** + * {@inheritdoc} + */ + public function xpath($query) + { + $nodesContainer = []; - /** - * {@inheritdoc} - */ - public function xpath($query) { - $nodesContainer = []; + if (is_object($this->xmlObject)) { + $nodes = $this->xmlObject->xpath($query); - if (is_object($this->xmlObject)) { - $nodes = $this->xmlObject->xpath($query); + if ($nodes) { + foreach ($nodes as $node) { + $nodesContainer[] = new SimpleXmlNode($node); + } + } + } - if ($nodes) { - foreach ($nodes as $node) { - $nodesContainer[] = new SimpleXmlNode($node); - } - } - } - - return $nodesContainer; - } + return $nodesContainer; + } } diff --git a/src/Lib/Xml/SimpleXmlNode.php b/src/Lib/Xml/SimpleXmlNode.php index c45645a9..d1c0fb84 100644 --- a/src/Lib/Xml/SimpleXmlNode.php +++ b/src/Lib/Xml/SimpleXmlNode.php @@ -6,106 +6,104 @@ * Class SimpleXmlNode * @package PP\Lib\Xml */ -class SimpleXmlNode extends AbstractXmlNode { - - /** - * {@inheritdoc} - */ - protected function _createXmlContext() { - if ($this->_xml instanceof AbstractXml) { - return; - } - - // dat is strange... - $this->_xml = new SimpleXml($this->_node); - } - - /** - * {@inheritdoc} - */ - public function nodeName() { - if (isset($this->_nodeName)) { - return $this->_nodeName; - } - - $this->_nodeName = $this->_node->getName(); - return $this->_nodeName; - } - - /** - * {@inheritdoc} - */ - public function nodeValue() { - if (isset($this->_nodeValue)) { - return $this->_nodeValue; - } - - $this->_nodeValue = (string)$this->_node; - return $this->_nodeValue; - } - - /** - * {@inheritdoc} - */ - public function nodeType() { - if (isset($this->_nodeType)) { - return $this->_nodeType; - } - - $n = $this->_node; - switch (true) { - case $n->xpath('/*') == [$n]; - $this->_nodeType = Xml::DOC; - break; - case ($n->xpath('.') == [$n]): - $this->_nodeType = Xml::ELEMENT; - break; - case $n->attributes() === null: - case $n[0] == $n: - $this->_nodeType = Xml::ATTRIBUTE; - break; - default: - $this->_nodeType = Xml::NONE; - break; - } - return $this->_nodeType; - } - - /** - * {@inheritdoc} - */ - public function nodeXValue($xpath) { - [$node] = (array)($this->_node->xpath($xpath)) + [0 => '']; +class SimpleXmlNode extends AbstractXmlNode +{ + /** + * {@inheritdoc} + */ + protected function _createXmlContext() + { + if ($this->_xml instanceof AbstractXml) { + return; + } + + // dat is strange... + $this->_xml = new SimpleXml($this->_node); + } + + /** + * {@inheritdoc} + */ + public function nodeName() + { + if (isset($this->_nodeName)) { + return $this->_nodeName; + } + + $this->_nodeName = $this->_node->getName(); + return $this->_nodeName; + } + + /** + * {@inheritdoc} + */ + public function nodeValue() + { + if (isset($this->_nodeValue)) { + return $this->_nodeValue; + } + + $this->_nodeValue = (string)$this->_node; + return $this->_nodeValue; + } + + /** + * {@inheritdoc} + */ + public function nodeType() + { + if (isset($this->_nodeType)) { + return $this->_nodeType; + } + + $n = $this->_node; + $this->_nodeType = match (true) { + $n->xpath('/*') == [$n] => Xml::DOC, + $n->xpath('.') == [$n] => Xml::ELEMENT, + $n->attributes() === null, $n[0] == $n => Xml::ATTRIBUTE, + default => Xml::NONE, + }; + return $this->_nodeType; + } + + /** + * {@inheritdoc} + */ + public function nodeXValue($xpath) + { + [$node] = (array)($this->_node->xpath($xpath)) + [0 => '']; return (string)$node; - } + } - /** - * {@inheritdoc} - */ - public function attributes() { - if (isset($this->_attributes)) { - return array_values($this->_attributes); - } + /** + * {@inheritdoc} + */ + public function attributes() + { + if (isset($this->_attributes)) { + return array_values($this->_attributes); + } - $this->_attributes = []; + $this->_attributes = []; if (is_object($this->_node)) { - $attrs = $this->_node->attributes(); - - if ($attrs) { - foreach ($attrs as $k => $v) { - $this->_attributes[trim($k)] = Xml::attributePrototype($k, (string)$v); - } - } - } - - return array_values($this->_attributes); - } - - /** - * {@inheritdoc} - */ - public function getChildObjects() { - return $this->_node->children(); - } + $attrs = $this->_node->attributes(); + + if ($attrs) { + foreach ($attrs as $k => $v) { + $this->_attributes[trim((string) $k)] = Xml::attributePrototype($k, (string)$v); + } + } + } + + return array_values($this->_attributes); + } + + /** + * {@inheritdoc} + */ + public function getChildObjects() + { + return $this->_node->children(); + } } diff --git a/src/Lib/Xml/Xml.php b/src/Lib/Xml/Xml.php index 6b8a1fcd..32a121b6 100644 --- a/src/Lib/Xml/Xml.php +++ b/src/Lib/Xml/Xml.php @@ -4,58 +4,51 @@ class Xml { - - public const NONE = 0; - public const ELEMENT = 1; - public const ATTRIBUTE = 2; - public const DOC = 9; - - /** @var SimpleXml */ - public $xml; - - /** - * Xml constructor. - * @param $xmlEntity - */ - public function __construct($xmlEntity) - { - - switch (true) { - case extension_loaded('simplexml'): - $this->xml = new SimpleXml($xmlEntity); - break; - - default: - $this->xml = (object)['xmlObject' => false]; - } - } - - /** - * @param $fileName - * @return bool|SimpleXml - */ - public static function load($fileName) - { - $instance = new Xml($fileName); - return $instance->xml->xmlObject ? $instance->xml : false; - } - - /** - * @param $xmlDataInString - * @return bool|object - */ - public static function loadString($xmlDataInString) - { - return Xml::load($xmlDataInString); - } - - /** - * @param $name - * @param $value - * @return object - */ - public static function attributePrototype($name, $value) - { - return (object)['name' => $name, 'value' => $value]; - } + public const NONE = 0; + public const ELEMENT = 1; + public const ATTRIBUTE = 2; + public const DOC = 9; + + /** @var SimpleXml */ + public $xml; + + /** + * Xml constructor. + * @param $xmlEntity + */ + public function __construct($xmlEntity) + { + + $this->xml = match (true) { + extension_loaded('simplexml') => new SimpleXml($xmlEntity), + default => (object)['xmlObject' => false], + }; + } + + /** + * @param $fileName + */ + public static function load($fileName): bool|\PP\Lib\Xml\SimpleXml + { + $instance = new Xml($fileName); + return $instance->xml->xmlObject ? $instance->xml : false; + } + + /** + * @param $xmlDataInString + */ + public static function loadString($xmlDataInString): bool|object + { + return Xml::load($xmlDataInString); + } + + /** + * @param $name + * @param $value + * @return object + */ + public static function attributePrototype($name, $value) + { + return (object)['name' => $name, 'value' => $value]; + } } diff --git a/src/Lib/Xml/XmlErrors.php b/src/Lib/Xml/XmlErrors.php index ad41f010..34a42948 100644 --- a/src/Lib/Xml/XmlErrors.php +++ b/src/Lib/Xml/XmlErrors.php @@ -2,33 +2,39 @@ namespace PP\Lib\Xml; -class XmlErrors { - public $errors; - - private function __construct() { - $this->errors = []; - } - - private function __clone() { - } - - public static function getErrors() { - $instance = self::getInstance(); - return $instance->errors; - } - - public static function addError($errno, $errstr, $errfile, $errline, $errcontext) { - $instance = self::getInstance(); - $instance->errors[] = $errstr; - } - - public static function getInstance() { - static $instance; - - if (!is_object($instance)) { - $instance = new self(); - } - - return $instance; - } +class XmlErrors +{ + public $errors; + + private function __construct() + { + $this->errors = []; + } + + private function __clone() + { + } + + public static function getErrors() + { + $instance = self::getInstance(); + return $instance->errors; + } + + public static function addError($errno, $errstr, $errfile, $errline, $errcontext) + { + $instance = self::getInstance(); + $instance->errors[] = $errstr; + } + + public static function getInstance() + { + static $instance; + + if (!is_object($instance)) { + $instance = new self(); + } + + return $instance; + } } diff --git a/src/Lib/Xml/XmlInterface.php b/src/Lib/Xml/XmlInterface.php index 5ba1835f..8e4d2f14 100644 --- a/src/Lib/Xml/XmlInterface.php +++ b/src/Lib/Xml/XmlInterface.php @@ -8,10 +8,9 @@ */ interface XmlInterface { - - /** - * @param string $query - * @return XmlNodeInterface[] - */ - public function xpath($query); + /** + * @param string $query + * @return XmlNodeInterface[] + */ + public function xpath($query); } diff --git a/src/Lib/Xml/XmlNodeInterface.php b/src/Lib/Xml/XmlNodeInterface.php index 2e6f54ec..cc487dab 100644 --- a/src/Lib/Xml/XmlNodeInterface.php +++ b/src/Lib/Xml/XmlNodeInterface.php @@ -8,62 +8,61 @@ */ interface XmlNodeInterface { + /** + * @return string + */ + public function nodeName(); - /** - * @return string - */ - public function nodeName(); + /** + * @return string + */ + public function nodeValue(); - /** - * @return string - */ - public function nodeValue(); + /** + * @param $xpath + * @return string + */ + public function nodeXValue($xpath); - /** - * @param $xpath - * @return string - */ - public function nodeXValue($xpath); + /** + * @return mixed + */ + public function nodeType(); - /** - * @return mixed - */ - public function nodeType(); + /** + * @return array + */ + public function attributes(); - /** - * @return array - */ - public function attributes(); + /** + * @param string $attrName + * @return mixed + */ + public function getAttribute($attrName); - /** - * @param string $attrName - * @return mixed - */ - public function getAttribute($attrName); + /** + * @return XmlNodeInterface[] + */ + public function childNodes(); - /** - * @return XmlNodeInterface[] - */ - public function childNodes(); + /** + * @param string $query + * @return XmlNodeInterface[] + */ + public function xpath($query); - /** - * @param string $query - * @return XmlNodeInterface[] - */ - public function xpath($query); + /** + * @return XmlNodeInterface[] + */ + public function getChildObjects(); - /** - * @return XmlNodeInterface[] - */ - public function getChildObjects(); + /** + * @return XmlNodeInterface + */ + public function parent(); - /** - * @return XmlNodeInterface - */ - public function parent(); - - /** - * @return bool - */ - public function isXmlNode(); + /** + * @return bool + */ + public function isXmlNode(); } diff --git a/src/Migration/MigrationAbstract.php b/src/Migration/MigrationAbstract.php index 41b50dab..326771be 100644 --- a/src/Migration/MigrationAbstract.php +++ b/src/Migration/MigrationAbstract.php @@ -8,40 +8,43 @@ * Class MigrationAbstract * @package PP\Migration */ -abstract class MigrationAbstract { +abstract class MigrationAbstract +{ + /** @var string[] */ + private array $sql; - /** @var string[] */ - private $sql; + /** + * @internal + */ + final public function __construct() + { + $this->sql = []; + } - /** - * @internal - */ - final public function __construct() { - $this->sql = []; - } + /** + * Use this method to write migrations + * + * @return void + */ + abstract public function up(); - /** - * Use this method to write migrations - * - * @return void - */ - abstract public function up(); + /** + * + * @param string $rawSql + * @return $this + */ + final protected function addSql($rawSql) + { + $this->sql[] = $rawSql; + return $this; + } - /** - * - * @param string $rawSql - * @return $this - */ - final protected function addSql($rawSql) { - $this->sql[] = $rawSql; - return $this; - } - - /** - * @return string[] - * @internal - */ - final public function getSqlList() { - return $this->sql; - } + /** + * @return string[] + * @internal + */ + final public function getSqlList() + { + return $this->sql; + } } diff --git a/src/Module/AbstractModule.php b/src/Module/AbstractModule.php index 7994eec6..be8eca08 100644 --- a/src/Module/AbstractModule.php +++ b/src/Module/AbstractModule.php @@ -11,114 +11,107 @@ */ abstract class AbstractModule implements ModuleInterface { - - use ContainerAwareTrait; - - public $area; - public $settings; - protected $__selfDescription; - - /** - * @var \PXApplication - */ - public $app; - - /** - * @var \PXDatabase|\PP\Lib\Database\Driver\PostgreSqlDriver - */ - public $db; - - /** - * @var \PXRequest - */ - public $request; - - /** - * @var \PXUser|\PXUserAuthorized - */ - public $user; - - /** - * @var \PP\Lib\Html\Layout\LayoutAbstract|\PP\Lib\Html\Layout\AdminHtmlLayout - */ - public $layout; - - /** - * @var \PP\Lib\Http\Response - */ - public $response; - - public function __construct($area, $settings, $selfDescription = null) - { - $this->area = $area; - $this->settings = $settings; - $this->__selfDescription = $selfDescription; //for module acl checks purposes - - \PXRegistry::assignToObject($this); - } - - /** - * @return array - */ - public static function getAclModuleActions() - { - $app = \PXRegistry::getApp(); - - return [ - 'viewmenu' => $app->langTree->getByPath('module_macl_rules.actions.viewmenu.rus'), - 'admin' => $app->langTree->getByPath('module_macl_rules.actions.admin.rus'), - ]; - } - - /** - * {@inheritdoc} - */ - public function adminIndex() - { - $this->layout->assignError('INNER.1.0', 'Функция adminIndex данного модуля не определена'); - } - - /** - * {@inheritdoc} - */ - public function adminPopup() - { - $this->layout->assignError('OUTER.CONTENT', 'Функция adminPopup данного модуля не определена'); - } - - /** - * {@inheritdoc} - */ - public function adminAction() - { - FatalError("Функция adminAction данного модуля не определена"); - } - - /** - * {@inheritdoc} - */ - public function userIndex() - { - } - - /** - * {@inheritdoc} - */ - public function userAction() - { - } - - /** - * {@inheritdoc} - */ - public function userJson() - { - } - - /** - * {@inheritdoc} - */ - public function adminJson() - { - } + use ContainerAwareTrait; + + /** + * @var \PXApplication + */ + public $app; + + /** + * @var \PXDatabase|\PP\Lib\Database\Driver\PostgreSqlDriver + */ + public $db; + + /** + * @var \PXRequest + */ + public $request; + + /** + * @var \PXUser|\PXUserAuthorized + */ + public $user; + + /** + * @var \PP\Lib\Html\Layout\LayoutAbstract|\PP\Lib\Html\Layout\AdminHtmlLayout + */ + public $layout; + + /** + * @var \PP\Lib\Http\Response + */ + public $response; + + public function __construct(public $area, public $settings, protected $__selfDescription = null) + { + //for module acl checks purposes + + \PXRegistry::assignToObject($this); + } + + /** + * @return array + */ + public static function getAclModuleActions() + { + $app = \PXRegistry::getApp(); + + return [ + 'viewmenu' => $app->langTree->getByPath('module_macl_rules.actions.viewmenu.rus'), + 'admin' => $app->langTree->getByPath('module_macl_rules.actions.admin.rus'), + ]; + } + + /** + * {@inheritdoc} + */ + public function adminIndex() + { + $this->layout->assignError('INNER.1.0', 'Функция adminIndex данного модуля не определена'); + } + + /** + * {@inheritdoc} + */ + public function adminPopup() + { + $this->layout->assignError('OUTER.CONTENT', 'Функция adminPopup данного модуля не определена'); + } + + /** + * {@inheritdoc} + */ + public function adminAction() + { + FatalError("Функция adminAction данного модуля не определена"); + } + + /** + * {@inheritdoc} + */ + public function userIndex() + { + } + + /** + * {@inheritdoc} + */ + public function userAction() + { + } + + /** + * {@inheritdoc} + */ + public function userJson() + { + } + + /** + * {@inheritdoc} + */ + public function adminJson() + { + } } diff --git a/src/Module/AclModule.php b/src/Module/AclModule.php index bdbfb09d..8ae61193 100644 --- a/src/Module/AclModule.php +++ b/src/Module/AclModule.php @@ -11,594 +11,580 @@ */ class AclModule extends AbstractModule { - public $what; - public $access; - public $objectRule = 'user'; - public $aclObjectTitle = 'Тип объекта'; - - protected $ruleTypeField; - protected $orderingField; - - public function __construct($area, $settings) - { - parent::__construct($area, $settings); - - $this->what = $this->getAvailableActions(); - $this->access = $this->getAvailableAccess(); - $this->sqlTable = 'acl_objects'; - $this->orderingField = 'sys_order'; - $this->ruleTypeField = 'objectrule'; - } - - public function getAvailableActions() - { - return $this->getAclDefinitionsFor('actions'); - } - - public function getAvailableAccess() - { - return $this->getAclDefinitionsFor('access'); - } - - public function getAclDefinitionsFor($what) - { - if (empty($this->app->langTree['module_acl_rules'][$what])) { - FatalError("Не описаны правила module_acl_rules[{$what}] в lang.yaml"); - } - - $_ = $this->app->langTree['module_acl_rules'][$what]; - return array_combine(array_keys($_), getColFromTable($_, 'rus')); //TODO: make lang choose optional - } - - public function getWhatDict() - { - return $this->what; - } - - public function adminIndex() - { - $sid = $this->_getSid(); - $rules = $this->_getRules($sid); - $fields = $this->_getFields(); - $dicts = $this->_getDicts(); - - $this->indexSetMenu($sid); - - $table = new \PXAdminTableSimple($fields); - - $table->setData($rules); - - $table->setNullText('Все'); - $table->setDict('sgroupid', $dicts['sgroup']); - $table->setDict('objecttype', $dicts['types']); - $table->setDict('what', $this->getWhatDict()); - $table->setDict('access', $this->access); - - $queryParams = 'area=' . $this->area . '&sid=' . urlencode($sid); - $table->setControls('/admin/popup.phtml?' . $queryParams . '&id=', 'изменить это правило', 'edit', false, true); - $table->setControls('/admin/action.phtml?' . $queryParams . '&action=delete&id=', 'удалить это правило', 'del', true, false); - - $table->setControls('/admin/action.phtml?' . $queryParams . '&action=up&id=', 'поднять это правило', 'up', false, false); - $table->setControls('/admin/action.phtml?' . $queryParams . '&action=down&id=', 'опустить это правило', 'down', false, false); - $table->showEven(); - - $table->setTableId('acl'); - - $table->addToParent('INNER.1.0'); - - $this->layout->setTwoColumns(false); - $this->layout->assign('INNER.1.1', $this->addNewRuleButton()); - } - - public function indexSetMenu($rSid) - { - $types = []; - - $countsQueryResult = $this->db->query(sprintf('SELECT "objecttype" as "id", count(*) as "count" FROM "%s" WHERE "objectrule" = \'%s\' GROUP BY 1;', $this->sqlTable, $this->objectRule)); - $counts = array_flat($countsQueryResult, 'id', 'count'); - - $types['*'] = ' Полный список (' . array_sum($counts) . ')'; - if ($counts['']) { - $types[''] = ' Все (' . $counts[''] . ')'; - } - - $objects = $this->objectRule === 'user' - ? $this->app->types - : $this->app->getAvailableModules(); - - foreach ($objects as $k => $v) { - if (!isset($counts[$k])) { - continue; - } - - if ($v instanceof \PXModuleDescription) { - $title = $v->getDescription() == '' || $v->getDescription() == \PXModuleDescription::EMPTY_DESCRIPTION - ? $v->getName() - : $v->getDescription(); - } else { - $title = (!empty($v->title) ? $v->title : (!empty($v->description) ? $v->description : (!empty($v->name) ? $v->name : $k))); - } - - $count = (int)$counts[$k]; - $types[$k] = "$title ($count)"; - } - - asort($types); - - if (!isset($types[$rSid])) { - $rSid = key($types); - } - - $this->layout->assignKeyValueList('INNER.0.0', $types, $rSid); - } - - public function addNewRuleButton() - { - $button = new \PXControlButton('Правило доступа'); - $button->setClickCode('Popup(\'' . '/admin/popup.phtml?area=' . $this->area . '\')'); - $button->setClass('add'); - return $button->html(); - } - - public function adminPopup() - { - $layout = $this->layout; - $request = $this->request; - $app = $this->app; - - $rules = $this->_getRules(); - $fields = $this->_getFields(); - $dicts = $this->_getDicts(); - - $rId = $request->getId(); - - $layout->SetGetVarToSave('id', $rId); - $layout->SetOuterForm('action.phtml', 'POST', 'multipart/form-data'); - - $object = []; - - if ($rId && isset($rules[$rId])) { - $object = $rules[$rId]; - } else { - foreach ($fields as $name => $title) { - $object[$name] = null; - } - } - - - //set save buttons - $form = new \PXAdminForm(null, null); - $form->leftControls(); - $form->rightControls(); - - $_ = ''; - $_ .= \NLAbstractHTMLForm::BuildHidden('id', $rId); - $_ .= \NLAbstractHTMLForm::BuildHidden('area', $this->area); - $_ .= \NLAbstractHTMLForm::BuildHidden('action', ($rId ? 'edit' : 'add')); - - $_ .= ''; - foreach ($fields as $col => $title) { - $p = []; - $fieldType = new \PXFieldDescription([], $app, $p); - $fieldType->name = $col; - $fieldType->description = $title; - $param = [ - 'parents' => NULL, - 'selfParents' => NULL, - 'even' => false, - ]; - - if (isset($dicts[$col])) { - $dType = 'DROPDOWN'; - if ($col == 'sgroupid' && $app->types['sgroup']->struct == 'tree') { - $param['datatype'] = &$app->types['sgroup']; - $param['root_title'] = '-- любая --'; - $dType = 'PARENTDROPDOWN'; - } - - $fieldType->setDisplayType($dType); - - $tmpVals = []; - foreach ($dicts[$col] as $id => $val) { - $tmpVals[] = ['id' => $id, 'title' => $val]; - } - - $directory = new \PXDirectoryDescription($col); - $directory->values = $tmpVals; - $directory->displayField = 'title'; - - $fieldType->values = $directory; - - } else { - $fieldType->setDisplayType('TEXT'); - } - - $_ .= $fieldType->displayType->buildRow($fieldType, $object, $param); - } - $_ .= '
'; - - $layout->append('OUTER.CONTENT', $_); - - $title = ($rId == 0) ? 'Добавление нового правила' : 'Редактирование правила №' . $rId; - $layout->assignTitle($title); - } - - public function _getFields() - { - return [ - 'sgroupid' => 'Группа', - 'objectid' => 'Объект', - 'objectparent' => 'Родитель объекта', - 'objecttype' => $this->aclObjectTitle, - 'what' => 'Действие', - 'access' => 'Доступ', - ]; - } - - public function _getSid() - { - return isset($_GET['sid']) ? $_GET['sid'] : '*'; - } - - public function _getSidCriteria($sid) - { - $andwhat = ['blank' => '1 = 1']; - - if ($sid === null || $sid === '*') { - return $andwhat; - } - - $sid = $this->db->escapeString($sid); - $andwhat['sid'] = sprintf(' COALESCE("objecttype", \'\') = \'%s\'', $sid); - - return $andwhat; - } - - public function _getRules($sid = null, $limit = null) - { - $criterias = $this->_getSidCriteria($sid); - $criterias[] = sprintf('"objectrule" = \'%s\'', $this->objectRule); - $where = join(' AND ', $criterias); - $limit = ($limit) ? [(int)$limit, 0] : null; - - $tmp = $this->db->query(sprintf('SELECT * FROM "%s" WHERE %s ORDER BY "%s" ASC;', $this->sqlTable, $where, $this->orderingField), false, $limit); - - return $limit == 1 ? reset($tmp) : array_flat($tmp, 'id'); - } - - public function _getTypes() - { - $types = []; - - foreach ($this->db->types as $typeName => $type) { - $types[$typeName] = $type->title; - } - - $types[null] = '-- любой --'; - - return $types; - } - - public function _getDicts() - { - $dicts = []; - - // sgroup - $this->db->LoadDirectoriesByType($this->db->types['sgroup']); - $dicts['sgroup'] = GetColFromTableWithIndexs($this->db->app->directory['sgroup']->values, 'title'); - $dicts['types'] = $this->_getTypes(); - $dicts['what'] = $this->getWhatDict(); - - $dicts['sgroupid'] =& $dicts['sgroup']; - $dicts['access'] =& $this->access; - $dicts['objecttype'] =& $dicts['types']; - - return $dicts; - } - - protected - function getRuleById($rId, $sid = false) - { - return current($this->getRulesByIds($rId, $sid)); - } - - protected - function getRulesByIds($ids, $sid = false) - { - $ids = array_filter(array_map('intval', (array)$ids)); - if (empty($ids)) { - return []; - } - - $criteria = $this->_getSidCriteria($sid); - $criteria['id'] = sprintf('"id" IN (%s)', join(',', $ids)); - $where = join(' AND ', $criteria); - - $query = sprintf('SELECT %s, id FROM %s WHERE %s;', $this->orderingField, $this->sqlTable, $where); - - return $this->db->query($query); - } - - - protected - function getRulesByRuleAndDirection($rule, $direction, $sid = false, $limit = 1) - { - if ($limit <= 0) { - return null; - } - - $db = $this->db; - - // combining query - $criteria = $this->_getSidCriteria($sid); - $criteria['rule'] = sprintf('"objectrule" = \'%s\'', $this->objectRule); - - // set where and order by $direction - switch ($direction) { - default: - case 'up': - $criteria['field'] = '"%1$s" < %2$d'; - $order = '"%1$s" DESC'; - break; - case 'down': - $criteria['field'] = '"%1$s" > %2$d'; - $order = '"%1$s" ASC'; - } - $criteria['field'] = sprintf($criteria['field'], $this->orderingField, $rule[$this->orderingField]); - - $where = join(' AND ', $criteria); - $order = sprintf($order, $this->orderingField); - $query = sprintf('SELECT "%s", "id" FROM %s WHERE %s ORDER BY %s ', $this->orderingField, $this->sqlTable, $where, $order); - - $objectsInDb = $db->query($query, false, [$limit, 0]); - if (!sizeof($objectsInDb)) { - return null; - } - - return $limit == 1 ? current($objectsInDb) : $objectsInDb; - } - - protected - function getFirstRule($sid = false) - { - return $this->_getRules($sid, 1); - } - - protected - function getRulesBetweenOrders($from, $to, $sid = false) - { - echo ($from . ' ' . $to) . PHP_EOL; - if ($from > $to) { - $from ^= $to ^= $from ^= $to; - } - - $criteria = $this->_getSidCriteria($sid); - $criteria['rule'] = sprintf('"objectrule" = \'%s\'', $this->objectRule); - $criteria['between'] = sprintf('"%s" BETWEEN %d AND %d', $this->orderingField, $from, $to); - $where = join(' AND ', $criteria); - - $query = sprintf('SELECT "%1$s", "id" FROM "%2$s" WHERE %3$s ORDER BY "%1$s" ASC;', $this->orderingField, $this->sqlTable, $where); - - return $this->db->query($query); - } - - protected - function swapOrders($a, $b) - { - $this->db->transactionBegin(); - $this->setOrder($a[$this->orderingField], $b['id']); - $this->setOrder($b[$this->orderingField], $a['id']); - $this->db->transactionCommit(); - - return true; // fixme. - } - - protected - function _moveRule($rId, $direction, $sid = false) - { - - $moving = $this->getRuleById($rId, $sid); - $passive = $this->getRulesByRuleAndDirection($moving, $direction, $sid); - - if ($passive) { - return $this->swapOrders($moving, $passive); - } - - return false; - } - - protected - function setOrder($order, $criteria) - { - if (($order[0] == '-' || $order[0] == '+') && ctype_digit(substr($order, 1))) { - // makes -1, +1 sql-readable - $order = sprintf('COALESCE("%s",0) %s', $this->orderingField, $order); - } - - switch (true) { - case (is_int($criteria) || ctype_digit($criteria)): - $where = '"id" = ' . $criteria; - break; - case (is_string($criteria) && ($criteria[0] == '<' || $criteria[0] == '>') && ctype_digit(substr($criteria, 1))): - case (is_string($criteria) && ($criteria[0] == '<' || $criteria[0] == '>') && ctype_digit(substr($criteria, 2)) && $criteria[1] == '='): - $where = $this->orderingField . $criteria; - break; - case is_array($criteria): - $where = join(' AND ', $criteria); - break; - default: - $where = $criteria; - } - - $set = ["{$this->orderingField} = {$order}"/*, "sys_modified = now()"*/]; - $set = join(",", $set); - - $query = "UPDATE {$this->sqlTable} SET {$set} WHERE {$where};"; - - // echo $query . PHP_EOL;return; - $this->db->modifyingQuery($query); - } - - protected - function getMaxOrder($sid) - { - static $maxOrder; - if ($maxOrder) { - return $maxOrder; - } - - $where = join(' AND ', $this->_getSidCriteria($sid)); - list(list($maxOrder)) = $this->db->query("SELECT max({$this->orderingField}) as \"0\" FROM {$this->sqlTable} WHERE {$where};"); - return $maxOrder; - } - - protected - function putRuleAfterRule($movingId, $afterId, $sid = false) - { - $db = $this->db; - - $rules = array_flat($this->getRulesByIds([$movingId, $afterId], $sid), 'id', $this->orderingField); - if (!array_key_exists($movingId, $rules)) { - return false; - } - - $movingOrder = $rules[$movingId]; - if (null === $movingOrder) { - $movingOrder = $this->getMaxOrder($sid); - } - - switch (true) { - case $afterId == 'first' || $afterId == 0: - $this->setOrder('+1', "<" . $movingOrder); - $this->setOrder(0, $movingId); - return true; - case is_int($afterId) || ctype_digit($afterId): - $afterOrder = $rules[$afterId]; - break; - default: - return 'after?!'; - } - - if ($afterOrder === null) { - // hm. cant be moved... - return 'do something with nulls!'; - } - - // fyi: all nulled at the end. _not at start_ - $oField = $this->orderingField; - - switch (true) { - case $movingOrder - 1 == $afterOrder: - // already correct order - break; - default: - case $movingOrder == $afterOrder: - // dunno... what we can do here? hm. - $this->setOrder('+1', $movingId); - // or that? - // $criteria['upper'] = sprintf('("%s" > %d OR "id" = %d)', $oField, $movingOrder, $moving['id']); - // $this->setOrder('+1', $criteria) - break; - case $movingOrder < $afterOrder: - // a 1 | a 1 | a 1 // set b after e - // b 2 | b 2 -> 4 | c 2 // moving = b, after = e - // c 3 -> 2 | c 2 | d 2 - // d 3 -> 2 | d 3 | e 3 - // e 4 -> 3 | e 4 | b 4 - $criteria = sprintf('%2$d < %1$s AND %1$s <= %3$d', $oField, $movingOrder, $afterOrder); - - $this->setOrder('-1', $criteria); - $this->setOrder($afterOrder, $movingId); - break; - case $movingOrder > $afterOrder: - // a 1 | a 1 | a 1 // set e after a (e.order > a.order) - // b 2 -> 3 | b 3 | e 2 // moving = e, after = a - // c 3 -> 4 | c 4 | b 3 - // d 3 -> 4 | d 4 | c 4 - // e 4 | e 4 -> 2 | d 4 - $criteria = sprintf('%2$d < %1$s AND %1$s <= %3$d', $oField, $afterOrder, $movingOrder); - - $this->setOrder('+1', $criteria); - $this->setOrder($afterOrder + 1, $movingId); - break; - } - - return compact('moving', 'after', 'changing', 'where'); - } - - public function adminAction() - { - $rId = (int)$this->request->getVar('id'); - $afterId = (int)$this->request->getVar('after'); - $sid = $this->_getSid(); - $redir = '/admin/?area=' . $this->area . '&sid=' . urlencode($sid); - $ajax = $this->request->getVar('ajax'); - $result = null; - - $action = $this->request->getVar('action'); - switch ($action) { - case 'up': - case 'down': - $result = $this->_moveRule($rId, $action, $sid); - break; - - // ajax - case 'putafter': - $result = $this->putRuleAfterRule($rId, $afterId, $sid); - break; - - case 'add': - $fields = array_keys($this->_getFields()); - $values = []; - - foreach ($fields as $field) { - $values[$field] = $this->request->getVar($field); - } - - $fields[] = $this->ruleTypeField; - $fields[] = 'sys_uuid'; - - $values[$this->ruleTypeField] = $this->objectRule; - $values['sys_uuid'] = Uuid::uuid4(); - - $rId = $this->db->InsertObject($this->sqlTable, $fields, $values); - $this->setOrder($rId, $rId); - - $redir = '/admin/popup.phtml?area=' . $this->area . '&id=' . $rId; - break; - - case 'edit': - $fields = array_keys($this->_getFields()); - $values = []; - - foreach ($fields as $field) { - $values[$field] = $this->request->getVar($field); - } - - $this->db->UpdateObjectById($this->sqlTable, $rId, $fields, $values); - - $redir = '/admin/popup.phtml?area=' . $this->area . '&sid=' . urlencode($sid) . '&id=' . $rId; - break; - - case 'delete': - $rule = $this->db->query('SELECT count(id) as count FROM ' . $this->sqlTable . ' WHERE id = ' . $rId); - - if (!isset($rule[0]['count'])) { - FatalError('Ошибка в таблице: ' . $this->sqlTable); - } - - if ((int)$rule[0]['count'] !== 1) { - FatalError('Правило с id = ' . $rId . ' в таблице ' . $this->sqlTable . ' не найдено'); - } - - $this->db->modifyingQuery('DELETE FROM ' . $this->sqlTable . ' WHERE id = ' . $rId); - break; - } - - if ($ajax) { - die(json_encode($result)); - } - - return $redir; - } + public $what; + public $access; + public $objectRule = 'user'; + public $aclObjectTitle = 'Тип объекта'; + + protected $ruleTypeField; + protected $orderingField; + + public function __construct($area, $settings) + { + parent::__construct($area, $settings); + + $this->what = $this->getAvailableActions(); + $this->access = $this->getAvailableAccess(); + $this->sqlTable = 'acl_objects'; + $this->orderingField = 'sys_order'; + $this->ruleTypeField = 'objectrule'; + } + + public function getAvailableActions() + { + return $this->getAclDefinitionsFor('actions'); + } + + public function getAvailableAccess() + { + return $this->getAclDefinitionsFor('access'); + } + + public function getAclDefinitionsFor($what) + { + if (empty($this->app->langTree['module_acl_rules'][$what])) { + FatalError("Не описаны правила module_acl_rules[{$what}] в lang.yaml"); + } + + $_ = $this->app->langTree['module_acl_rules'][$what]; + return array_combine(array_keys($_), getColFromTable($_, 'rus')); //TODO: make lang choose optional + } + + public function getWhatDict() + { + return $this->what; + } + + public function adminIndex() + { + $sid = $this->_getSid(); + $rules = $this->_getRules($sid); + $fields = $this->_getFields(); + $dicts = $this->_getDicts(); + + $this->indexSetMenu($sid); + + $table = new \PXAdminTableSimple($fields); + + $table->setData($rules); + + $table->setNullText('Все'); + $table->setDict('sgroupid', $dicts['sgroup']); + $table->setDict('objecttype', $dicts['types']); + $table->setDict('what', $this->getWhatDict()); + $table->setDict('access', $this->access); + + $queryParams = 'area=' . $this->area . '&sid=' . urlencode((string) $sid); + $table->setControls('/admin/popup.phtml?' . $queryParams . '&id=', 'изменить это правило', 'edit', false, true); + $table->setControls('/admin/action.phtml?' . $queryParams . '&action=delete&id=', 'удалить это правило', 'del', true, false); + + $table->setControls('/admin/action.phtml?' . $queryParams . '&action=up&id=', 'поднять это правило', 'up', false, false); + $table->setControls('/admin/action.phtml?' . $queryParams . '&action=down&id=', 'опустить это правило', 'down', false, false); + $table->showEven(); + + $table->setTableId('acl'); + + $table->addToParent('INNER.1.0'); + + $this->layout->setTwoColumns(false); + $this->layout->assign('INNER.1.1', $this->addNewRuleButton()); + } + + public function indexSetMenu($rSid) + { + $types = []; + + $countsQueryResult = $this->db->query(sprintf('SELECT "objecttype" as "id", count(*) as "count" FROM "%s" WHERE "objectrule" = \'%s\' GROUP BY 1;', $this->sqlTable, $this->objectRule)); + $counts = array_flat($countsQueryResult, 'id', 'count'); + + $types['*'] = ' Полный список (' . array_sum($counts) . ')'; + if ($counts['']) { + $types[''] = ' Все (' . $counts[''] . ')'; + } + + $objects = $this->objectRule === 'user' + ? $this->app->types + : $this->app->getAvailableModules(); + + foreach ($objects as $k => $v) { + if (!isset($counts[$k])) { + continue; + } + + if ($v instanceof \PXModuleDescription) { + $title = $v->getDescription() == '' || $v->getDescription() == \PXModuleDescription::EMPTY_DESCRIPTION + ? $v->getName() + : $v->getDescription(); + } else { + $title = (!empty($v->title) ? $v->title : (!empty($v->description) ? $v->description : (!empty($v->name) ? $v->name : $k))); + } + + $count = (int)$counts[$k]; + $types[$k] = "$title ($count)"; + } + + asort($types); + + if (!isset($types[$rSid])) { + $rSid = key($types); + } + + $this->layout->assignKeyValueList('INNER.0.0', $types, $rSid); + } + + public function addNewRuleButton() + { + $button = new \PXControlButton('Правило доступа'); + $button->setClickCode('Popup(\'' . '/admin/popup.phtml?area=' . $this->area . '\')'); + $button->setClass('add'); + return $button->html(); + } + + public function adminPopup() + { + $layout = $this->layout; + $request = $this->request; + $app = $this->app; + + $rules = $this->_getRules(); + $fields = $this->_getFields(); + $dicts = $this->_getDicts(); + + $rId = $request->getId(); + + $layout->SetGetVarToSave('id', $rId); + $layout->SetOuterForm('action.phtml', 'POST', 'multipart/form-data'); + + $object = []; + + if ($rId && isset($rules[$rId])) { + $object = $rules[$rId]; + } else { + foreach ($fields as $name => $title) { + $object[$name] = null; + } + } + + + //set save buttons + $form = new \PXAdminForm(null, null); + $form->leftControls(); + $form->rightControls(); + + $_ = ''; + $_ .= \NLAbstractHTMLForm::BuildHidden('id', $rId); + $_ .= \NLAbstractHTMLForm::BuildHidden('area', $this->area); + $_ .= \NLAbstractHTMLForm::BuildHidden('action', ($rId ? 'edit' : 'add')); + + $_ .= ''; + foreach ($fields as $col => $title) { + $p = []; + $fieldType = new \PXFieldDescription([], $app, $p); + $fieldType->name = $col; + $fieldType->description = $title; + $param = [ + 'parents' => null, + 'selfParents' => null, + 'even' => false, + ]; + + if (isset($dicts[$col])) { + $dType = 'DROPDOWN'; + if ($col == 'sgroupid' && $app->types['sgroup']->struct == 'tree') { + $param['datatype'] = &$app->types['sgroup']; + $param['root_title'] = '-- любая --'; + $dType = 'PARENTDROPDOWN'; + } + + $fieldType->setDisplayType($dType); + + $tmpVals = []; + foreach ($dicts[$col] as $id => $val) { + $tmpVals[] = ['id' => $id, 'title' => $val]; + } + + $directory = new \PXDirectoryDescription($col); + $directory->values = $tmpVals; + $directory->displayField = 'title'; + + $fieldType->values = $directory; + + } else { + $fieldType->setDisplayType('TEXT'); + } + + $_ .= $fieldType->displayType->buildRow($fieldType, $object, $param); + } + $_ .= '
'; + + $layout->append('OUTER.CONTENT', $_); + + $title = ($rId == 0) ? 'Добавление нового правила' : 'Редактирование правила №' . $rId; + $layout->assignTitle($title); + } + + public function _getFields() + { + return [ + 'sgroupid' => 'Группа', + 'objectid' => 'Объект', + 'objectparent' => 'Родитель объекта', + 'objecttype' => $this->aclObjectTitle, + 'what' => 'Действие', + 'access' => 'Доступ', + ]; + } + + public function _getSid() + { + return $_GET['sid'] ?? '*'; + } + + public function _getSidCriteria($sid) + { + $andwhat = ['blank' => '1 = 1']; + + if ($sid === null || $sid === '*') { + return $andwhat; + } + + $sid = $this->db->escapeString($sid); + $andwhat['sid'] = sprintf(' COALESCE("objecttype", \'\') = \'%s\'', $sid); + + return $andwhat; + } + + public function _getRules($sid = null, $limit = null) + { + $criterias = $this->_getSidCriteria($sid); + $criterias[] = sprintf('"objectrule" = \'%s\'', $this->objectRule); + $where = join(' AND ', $criterias); + $limit = ($limit) ? [(int)$limit, 0] : null; + + $tmp = $this->db->query(sprintf('SELECT * FROM "%s" WHERE %s ORDER BY "%s" ASC;', $this->sqlTable, $where, $this->orderingField), false, $limit); + + return $limit == 1 ? reset($tmp) : array_flat($tmp, 'id'); + } + + public function _getTypes() + { + $types = []; + + foreach ($this->db->types as $typeName => $type) { + $types[$typeName] = $type->title; + } + + $types[null] = '-- любой --'; + + return $types; + } + + public function _getDicts() + { + $dicts = []; + + // sgroup + $this->db->LoadDirectoriesByType($this->db->types['sgroup']); + $dicts['sgroup'] = GetColFromTableWithIndexs($this->db->app->directory['sgroup']->values, 'title'); + $dicts['types'] = $this->_getTypes(); + $dicts['what'] = $this->getWhatDict(); + + $dicts['sgroupid'] = & $dicts['sgroup']; + $dicts['access'] = & $this->access; + $dicts['objecttype'] = & $dicts['types']; + + return $dicts; + } + + protected function getRuleById($rId, $sid = false) + { + return current($this->getRulesByIds($rId, $sid)); + } + + protected function getRulesByIds($ids, $sid = false) + { + $ids = array_filter(array_map('intval', (array)$ids)); + if (empty($ids)) { + return []; + } + + $criteria = $this->_getSidCriteria($sid); + $criteria['id'] = sprintf('"id" IN (%s)', join(',', $ids)); + $where = join(' AND ', $criteria); + + $query = sprintf('SELECT %s, id FROM %s WHERE %s;', $this->orderingField, $this->sqlTable, $where); + + return $this->db->query($query); + } + + + protected function getRulesByRuleAndDirection($rule, $direction, $sid = false, $limit = 1) + { + if ($limit <= 0) { + return null; + } + + $db = $this->db; + + // combining query + $criteria = $this->_getSidCriteria($sid); + $criteria['rule'] = sprintf('"objectrule" = \'%s\'', $this->objectRule); + + // set where and order by $direction + switch ($direction) { + default: + case 'up': + $criteria['field'] = '"%1$s" < %2$d'; + $order = '"%1$s" DESC'; + break; + case 'down': + $criteria['field'] = '"%1$s" > %2$d'; + $order = '"%1$s" ASC'; + } + $criteria['field'] = sprintf($criteria['field'], $this->orderingField, $rule[$this->orderingField]); + + $where = join(' AND ', $criteria); + $order = sprintf($order, $this->orderingField); + $query = sprintf('SELECT "%s", "id" FROM %s WHERE %s ORDER BY %s ', $this->orderingField, $this->sqlTable, $where, $order); + + $objectsInDb = $db->query($query, false, [$limit, 0]); + if (!sizeof($objectsInDb)) { + return null; + } + + return $limit == 1 ? current($objectsInDb) : $objectsInDb; + } + + protected function getFirstRule($sid = false) + { + return $this->_getRules($sid, 1); + } + + protected function getRulesBetweenOrders($from, $to, $sid = false) + { + echo($from . ' ' . $to) . PHP_EOL; + if ($from > $to) { + $from ^= $to ^= $from ^= $to; + } + + $criteria = $this->_getSidCriteria($sid); + $criteria['rule'] = sprintf('"objectrule" = \'%s\'', $this->objectRule); + $criteria['between'] = sprintf('"%s" BETWEEN %d AND %d', $this->orderingField, $from, $to); + $where = join(' AND ', $criteria); + + $query = sprintf('SELECT "%1$s", "id" FROM "%2$s" WHERE %3$s ORDER BY "%1$s" ASC;', $this->orderingField, $this->sqlTable, $where); + + return $this->db->query($query); + } + + protected function swapOrders($a, $b) + { + $this->db->transactionBegin(); + $this->setOrder($a[$this->orderingField], $b['id']); + $this->setOrder($b[$this->orderingField], $a['id']); + $this->db->transactionCommit(); + + return true; // fixme. + } + + protected function _moveRule($rId, $direction, $sid = false) + { + + $moving = $this->getRuleById($rId, $sid); + $passive = $this->getRulesByRuleAndDirection($moving, $direction, $sid); + + if ($passive) { + return $this->swapOrders($moving, $passive); + } + + return false; + } + + protected function setOrder($order, $criteria) + { + if (($order[0] == '-' || $order[0] == '+') && ctype_digit(substr((string) $order, 1))) { + // makes -1, +1 sql-readable + $order = sprintf('COALESCE("%s",0) %s', $this->orderingField, $order); + } + + $where = match (true) { + is_int($criteria) || ctype_digit((string) $criteria) => '"id" = ' . $criteria, + is_string($criteria) && ($criteria[0] == '<' || $criteria[0] == '>') && ctype_digit(substr($criteria, 1)), is_string($criteria) && ($criteria[0] == '<' || $criteria[0] == '>') && ctype_digit(substr($criteria, 2)) && $criteria[1] == '=' => $this->orderingField . $criteria, + is_array($criteria) => join(' AND ', $criteria), + default => $criteria, + }; + + $set = ["{$this->orderingField} = {$order}"/*, "sys_modified = now()"*/]; + $set = join(",", $set); + + $query = "UPDATE {$this->sqlTable} SET {$set} WHERE {$where};"; + + // echo $query . PHP_EOL;return; + $this->db->modifyingQuery($query); + } + + protected function getMaxOrder($sid) + { + static $maxOrder; + if ($maxOrder) { + return $maxOrder; + } + + $where = join(' AND ', $this->_getSidCriteria($sid)); + [[$maxOrder]] = $this->db->query("SELECT max({$this->orderingField}) as \"0\" FROM {$this->sqlTable} WHERE {$where};"); + return $maxOrder; + } + + protected function putRuleAfterRule($movingId, $afterId, $sid = false) + { + $db = $this->db; + $moving = null; + $after = null; + $changing = null; + $where = null; + + $rules = array_flat($this->getRulesByIds([$movingId, $afterId], $sid), 'id', $this->orderingField); + if (!array_key_exists($movingId, $rules)) { + return false; + } + + $movingOrder = $rules[$movingId]; + if (null === $movingOrder) { + $movingOrder = $this->getMaxOrder($sid); + } + + switch (true) { + case $afterId == 'first' || $afterId == 0: + $this->setOrder('+1', "<" . $movingOrder); + $this->setOrder(0, $movingId); + return true; + case is_int($afterId) || ctype_digit((string) $afterId): + $afterOrder = $rules[$afterId]; + break; + default: + return 'after?!'; + } + + if ($afterOrder === null) { + // hm. cant be moved... + return 'do something with nulls!'; + } + + // fyi: all nulled at the end. _not at start_ + $oField = $this->orderingField; + + switch (true) { + case $movingOrder - 1 == $afterOrder: + // already correct order + break; + default: + case $movingOrder == $afterOrder: + // dunno... what we can do here? hm. + $this->setOrder('+1', $movingId); + // or that? + // $criteria['upper'] = sprintf('("%s" > %d OR "id" = %d)', $oField, $movingOrder, $moving['id']); + // $this->setOrder('+1', $criteria) + break; + case $movingOrder < $afterOrder: + // a 1 | a 1 | a 1 // set b after e + // b 2 | b 2 -> 4 | c 2 // moving = b, after = e + // c 3 -> 2 | c 2 | d 2 + // d 3 -> 2 | d 3 | e 3 + // e 4 -> 3 | e 4 | b 4 + $criteria = sprintf('%2$d < %1$s AND %1$s <= %3$d', $oField, $movingOrder, $afterOrder); + + $this->setOrder('-1', $criteria); + $this->setOrder($afterOrder, $movingId); + break; + case $movingOrder > $afterOrder: + // a 1 | a 1 | a 1 // set e after a (e.order > a.order) + // b 2 -> 3 | b 3 | e 2 // moving = e, after = a + // c 3 -> 4 | c 4 | b 3 + // d 3 -> 4 | d 4 | c 4 + // e 4 | e 4 -> 2 | d 4 + $criteria = sprintf('%2$d < %1$s AND %1$s <= %3$d', $oField, $afterOrder, $movingOrder); + + $this->setOrder('+1', $criteria); + $this->setOrder($afterOrder + 1, $movingId); + break; + } + + return compact('moving', 'after', 'changing', 'where'); + } + + public function adminAction() + { + $rId = (int)$this->request->getVar('id'); + $afterId = (int)$this->request->getVar('after'); + $sid = $this->_getSid(); + $redir = '/admin/?area=' . $this->area . '&sid=' . urlencode((string) $sid); + $ajax = $this->request->getVar('ajax'); + $result = null; + + $action = $this->request->getVar('action'); + switch ($action) { + case 'up': + case 'down': + $result = $this->_moveRule($rId, $action, $sid); + break; + + // ajax + case 'putafter': + $result = $this->putRuleAfterRule($rId, $afterId, $sid); + break; + + case 'add': + $fields = array_keys($this->_getFields()); + $values = []; + + foreach ($fields as $field) { + $values[$field] = $this->request->getVar($field); + } + + $fields[] = $this->ruleTypeField; + $fields[] = 'sys_uuid'; + + $values[$this->ruleTypeField] = $this->objectRule; + $values['sys_uuid'] = Uuid::uuid4(); + + $rId = $this->db->InsertObject($this->sqlTable, $fields, $values); + $this->setOrder($rId, $rId); + + $redir = '/admin/popup.phtml?area=' . $this->area . '&id=' . $rId; + break; + + case 'edit': + $fields = array_keys($this->_getFields()); + $values = []; + + foreach ($fields as $field) { + $values[$field] = $this->request->getVar($field); + } + + $this->db->UpdateObjectById($this->sqlTable, $rId, $fields, $values); + + $redir = '/admin/popup.phtml?area=' . $this->area . '&sid=' . urlencode((string) $sid) . '&id=' . $rId; + break; + + case 'delete': + $rule = $this->db->query('SELECT count(id) as count FROM ' . $this->sqlTable . ' WHERE id = ' . $rId); + + if (!isset($rule[0]['count'])) { + FatalError('Ошибка в таблице: ' . $this->sqlTable); + } + + if ((int)$rule[0]['count'] !== 1) { + FatalError('Правило с id = ' . $rId . ' в таблице ' . $this->sqlTable . ' не найдено'); + } + + $this->db->modifyingQuery('DELETE FROM ' . $this->sqlTable . ' WHERE id = ' . $rId); + break; + } + + if ($ajax) { + die(json_encode($result)); + } + + return $redir; + } } diff --git a/src/Module/AuditLogModule.php b/src/Module/AuditLogModule.php index 65d1b0de..2ad46de9 100644 --- a/src/Module/AuditLogModule.php +++ b/src/Module/AuditLogModule.php @@ -9,22 +9,21 @@ */ class AuditLogModule extends AbstractModule { + public function __construct($area, $settings) + { + parent::__construct($area, $settings); - public function __construct($area, $settings) - { - parent::__construct($area, $settings); + if (is_callable($this->layout->setOneColumn(...))) { + $this->layout->setOneColumn(); + } + } - if (is_callable([$this->layout, 'setOneColumn'])) { - $this->layout->setOneColumn(); - } - } + public function adminIndex() + { + require_once PPCOREPATH . 'lib/Logger/Audit/wrapper.class.inc'; - public function adminIndex() - { - require_once PPCOREPATH . 'lib/Logger/Audit/wrapper.class.inc'; - - $auditWrapper = new \PXAdminAuditWrapper(); - $auditWrapper->init_and_render(); - } + $auditWrapper = new \PXAdminAuditWrapper(); + $auditWrapper->init_and_render(); + } } diff --git a/src/Module/AuthModule.php b/src/Module/AuthModule.php index 1b9d8188..74bdf35a 100644 --- a/src/Module/AuthModule.php +++ b/src/Module/AuthModule.php @@ -11,7 +11,6 @@ */ class AuthModule extends AbstractModule { - public function adminIndex() { @@ -38,7 +37,7 @@ public function adminIndex() ], [ 'login' => $this->user->login, - 'passwd' => NULL, + 'passwd' => null, 'referer' => $this->request->getReferer(), 'area' => 'auth', 'captchaKey' => $captchaKey, @@ -54,7 +53,7 @@ public function adminIndex() ); - } else if ($this->request->getAction() == 'error') { + } elseif ($this->request->getAction() == 'error') { $this->layout->assign( 'LOGIN.ERROR', '' @@ -83,9 +82,9 @@ public function adminAction() } else { $nextLocation = $this->request->getReferer(); - if (strpos($nextLocation, '?') !== FALSE) { - if (strpos($nextLocation, '?action=') !== FALSE || strpos($nextLocation, '&action=') !== FALSE) { - $nextLocation = preg_replace("/(?<=\&|\?)action=[^&]*/", "action=error", $nextLocation); + if (str_contains((string) $nextLocation, '?')) { + if (str_contains((string) $nextLocation, '?action=') || str_contains((string) $nextLocation, '&action=')) { + $nextLocation = preg_replace("/(?<=\&|\?)action=[^&]*/", "action=error", (string) $nextLocation); } else { $nextLocation .= '&action=error'; } @@ -101,16 +100,12 @@ public function adminAction() public function userAction() { if ($this->_auth() == 0) { - $nextLocation = ($this->request->GetVar('onsuccess')) - ? $this->request->GetVar('onsuccess') - : $this->request->getReferer(); + $nextLocation = $this->request->GetVar('onsuccess') ?: $this->request->getReferer(); $nextLocation = removeParamFromUrl($nextLocation, 'login'); } else { - $nextLocation = ($this->request->GetVar('onerror')) - ? $this->request->GetVar('onerror') - : $this->request->getReferer(); + $nextLocation = $this->request->GetVar('onerror') ?: $this->request->getReferer(); $nextLocation = appendParamToUrl($nextLocation, 'login', 'bad'); } diff --git a/src/Module/CronRunModule.php b/src/Module/CronRunModule.php index 662ad2cf..c18f8743 100644 --- a/src/Module/CronRunModule.php +++ b/src/Module/CronRunModule.php @@ -18,312 +18,311 @@ */ class CronRunModule extends AbstractModule { - protected $timeFormat = 'd-m-Y H:i:s'; - - /** @var array */ - public $rules; - - /** @var array */ - public $job2rule; - - /** @var array */ - public $jobs; - - /** @var string */ - protected $resultsFile; - - /** @var string */ - protected $tempDir; - - public function __construct($area, $settings) - { - parent::__construct($area, $settings); - - $this->resultsFile = RUNTIME_PATH . 'cron.results'; - $this->tempDir = RUNTIME_PATH . 'lock' . DIRECTORY_SEPARATOR . 'cronrun'; - $this->rules = []; - $this->job2rule = []; - $this->jobs = []; - - $this->_parseRules($settings); - } - - public function _parseRules($settings) - { - if (!isset($settings['rule']) || !count($settings['rule'])) { - return; - } - - $count = 0; - foreach ($settings['rule'] as $s) { - if (!preg_match("/^\s*(.+?)\s+(\w+?)\s*$/", $s, $m)) { - continue; - } - - $rule = new CronRule($m[1]); - $jobName = $m[2]; - - if (!$rule->valid) { - continue; - } - - $file = strtolower($jobName) . '.cronrun.inc'; - - if (!class_exists(sprintf("pxcronrun%s", strtolower($jobName)), false)) { - if (file_exists(BASEPATH . '/local/cronruns/' . $file)) { - include_once BASEPATH . '/local/cronruns/' . $file; - } elseif (file_exists(BASEPATH . '/libpp/cronruns/' . $file)) { - include_once BASEPATH . '/libpp/cronruns/' . $file; - } else { - continue; - } - } - - $class = 'PXCronRun' . $jobName; - $instance = new $class(); - - $rm = [ - 'rule' => $rule, - 'jobhash' => $rule->matchHash . md5($jobName), - 'jobname' => strtolower($jobName), - 'job' => $instance, - ]; - - $this->rules[$count] = $rm; - $this->jobs[$jobName] = &$this->rules[$count]; - $this->job2rule[$rm['jobhash']] = $count; - $count++; - } - } - - public function runJob(&$job, &$app, $matchedTime) - { - MakeDirIfNotExists($this->tempDir); - - // ******* child code ****** - $fname = $this->tempDir . DIRECTORY_SEPARATOR . $job['jobname'] . '.lock'; - $fp = @fopen($fname, 'r'); - - if (!$fp) { - $fp = fopen($fname, 'w'); - } - - $st = flock($fp, LOCK_EX | LOCK_NB); - if (!$st) { - $fatal = true; - - if (isset($job['job']->longrunner)) { - $tmp = intval($job['job']->longrunner); - $mtime = fstat($fp); - $mtime = $mtime['mtime']; - - if (time() - $mtime < $tmp) { - $fatal = false; - } - } - - flock($fp, LOCK_UN); - fclose($fp); - - if ($fatal) { - FatalError('Cant lock tmp lock file: ' . $job['jobname']); - } - # else: Another instance of job is running and it's OK - exit(); - } - - touch($fname); # update mtime - $tmStart = time(); - - // !!! run code here !!! - // add NEW connect to the database from current child - $db = new PXDatabase($app); - $user = new PXUserCron(); - $db->setUser($user); - - PXRegistry::setDB($db); - PXRegistry::setUser($user); - - $db->loadDirectoriesAutomatic($app->directory); - if (isset($app->types[DT_STRUCT])) { - $tree = new Tree($db->getObjects($app->types[DT_STRUCT], true)); - } else { - $tree = null; - } - - if ($job['job'] instanceof ContainerAwareInterface) { - $job['job']->setContainer($this->container); - } - - /** @var AbstractCron $jobInstance */ - $jobInstance = $job['job']; - $res = $jobInstance->Run($app, $db, $tree, $matchedTime, $job['rule']); - - // !!! run code here !!! - $tmEnd = time(); - flock($fp, LOCK_UN); - fclose($fp); - - $cronStat = []; - $fp = @fopen($this->resultsFile, "r+"); - - if ($fp) { - do { - //nothing; - } while (!flock($fp, LOCK_EX)); - - fseek($fp, 0, SEEK_END); - $fsize = ftell($fp); - rewind($fp); - - if ($fsize > 0) { - $tStat = unserialize(fread($fp, $fsize)); - ftruncate($fp, 0); - rewind($fp); - - foreach ($tStat as $tHash => $vHash) { - if (isset($this->job2rule[$tHash])) { - $cronStat[$tHash] = $vHash; - } - } - } - - } else { - $fp = @fopen($this->resultsFile, "w"); - - if ($fp) { - do { - // nothing; - } while (!flock($fp, LOCK_EX)); - } - } - - $cronStat[$job['jobhash']] = ['start' => $tmStart, 'end' => $tmEnd, 'result' => $res]; - - fwrite($fp, serialize($cronStat)); - flock($fp, LOCK_UN); - fclose($fp); - } - - public function RunTasks(&$app, $matchedTime) - { - $t = localtime($matchedTime, true); - $pid = -1; - - foreach ($this->rules as $k) { - if ($this->isNotMatchTime($k['rule']->match, $t)) { - continue; - } - - if (($pid = pcntl_fork()) == 0) { - $this->runJob($k, $app, $matchedTime); //child code - exit(); //close child process - } - } - - if ($pid) { //parent code - while (pcntl_waitpid(-1, $status) > 0) { - //wanna some status ? - } - } - } - - public function isNotMatchTime($match, $t) - { - return !( - isset($match['min'][$t['tm_min']]) && - isset($match['hour'][$t['tm_hour']]) && - isset($match['mday'][$t['tm_mday']]) && - isset($match['mon'][$t['tm_mon'] + 1]) && - isset($match['wday'][$t['tm_wday']]) - ); - } - - private function _loadStat() - { - $fp = @fopen($this->resultsFile, 'r'); - - if ($fp) { - flock($fp, LOCK_EX); - - fseek($fp, 0, SEEK_END); - $fsize = ftell($fp); - rewind($fp); - $cronStat = unserialize(fread($fp, $fsize)); - - flock($fp, LOCK_UN); - fclose($fp); - - } else { - $cronStat = []; - } - - return $cronStat; - } - - public function getStat($job) - { - static $cronStat; - - if (is_null($cronStat)) { - $cronStat = $this->_loadStat(); - } - - if (isset($cronStat[$job['jobhash']])) { - $t = $cronStat[$job['jobhash']]; - } else { - $t = NULL; - } - - return $t; - } - - public function adminIndex() - { - $layout = $this->layout; - - $layout->setOneColumn(); - $layout->assign('INNER.0.1', 'Обновить'); - - $fields = [ - 'rule' => 'Правило', - 'name' => 'Название задачи', - 'time' => 'Начало/Окончание', - 'comment' => 'Примечание', - ]; - - $result = []; - - foreach ($this->rules as $k) { - $_ = []; - - $_['rule'] = $k['rule']->asString; - $_['name'] = $k['job']->name; - - - $t = $this->getStat($k); - - if (!is_null($t)) { - $diff = $t['end'] - $t['start']; - $dmin = (int)($diff / 60); - $dsec = $diff - $dmin * 60; - - $_['time'] = date($this->timeFormat, $t['start']) . '
' . date($this->timeFormat, $t['end']) . ' (' . $dmin . ' min ' . $dsec . ' sec)'; - $note = isset($t['result']['note']) ? htmlentities($t['result']['note'], ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) : ''; - $_['comment'] = ($t['result']['status'] >= 0) ? '' . $note . '' : '' . $note . ''; - - } else { - $_['time'] = ' 
 '; - $_['comment'] = 'Еще не запускалась'; - } - - $result[] = $_; - } + protected $timeFormat = 'd-m-Y H:i:s'; + + /** @var array */ + public $rules; + + /** @var array */ + public $job2rule; + + /** @var array */ + public $jobs; + + /** @var string */ + protected $resultsFile; + + /** @var string */ + protected $tempDir; + + public function __construct($area, $settings) + { + parent::__construct($area, $settings); + + $this->resultsFile = RUNTIME_PATH . 'cron.results'; + $this->tempDir = RUNTIME_PATH . 'lock' . DIRECTORY_SEPARATOR . 'cronrun'; + $this->rules = []; + $this->job2rule = []; + $this->jobs = []; + + $this->_parseRules($settings); + } + + public function _parseRules($settings) + { + if (!isset($settings['rule']) || !(is_countable($settings['rule']) ? count($settings['rule']) : 0)) { + return; + } + + $count = 0; + foreach ($settings['rule'] as $s) { + if (!preg_match("/^\s*(.+?)\s+(\w+?)\s*$/", (string) $s, $m)) { + continue; + } + + $rule = new CronRule($m[1]); + $jobName = $m[2]; + + if (!$rule->valid) { + continue; + } + + $file = strtolower($jobName) . '.cronrun.inc'; + + if (!class_exists(sprintf("pxcronrun%s", strtolower($jobName)), false)) { + if (file_exists(BASEPATH . '/local/cronruns/' . $file)) { + include_once BASEPATH . '/local/cronruns/' . $file; + } elseif (file_exists(BASEPATH . '/libpp/cronruns/' . $file)) { + include_once BASEPATH . '/libpp/cronruns/' . $file; + } else { + continue; + } + } + + $class = 'PXCronRun' . $jobName; + $instance = new $class(); + + $rm = [ + 'rule' => $rule, + 'jobhash' => $rule->matchHash . md5($jobName), + 'jobname' => strtolower($jobName), + 'job' => $instance, + ]; + + $this->rules[$count] = $rm; + $this->jobs[$jobName] = &$this->rules[$count]; + $this->job2rule[$rm['jobhash']] = $count; + $count++; + } + } + + public function runJob(&$job, &$app, $matchedTime) + { + MakeDirIfNotExists($this->tempDir); + + // ******* child code ****** + $fname = $this->tempDir . DIRECTORY_SEPARATOR . $job['jobname'] . '.lock'; + $fp = @fopen($fname, 'r'); + + if (!$fp) { + $fp = fopen($fname, 'w'); + } + + $st = flock($fp, LOCK_EX | LOCK_NB); + if (!$st) { + $fatal = true; + + if (isset($job['job']->longrunner)) { + $tmp = intval($job['job']->longrunner); + $mtime = fstat($fp); + $mtime = $mtime['mtime']; + + if (time() - $mtime < $tmp) { + $fatal = false; + } + } + + flock($fp, LOCK_UN); + fclose($fp); + + if ($fatal) { + FatalError('Cant lock tmp lock file: ' . $job['jobname']); + } + # else: Another instance of job is running and it's OK + exit(); + } + + touch($fname); # update mtime + $tmStart = time(); + + // !!! run code here !!! + // add NEW connect to the database from current child + $db = new PXDatabase($app); + $user = new PXUserCron(); + $db->setUser($user); + + PXRegistry::setDB($db); + PXRegistry::setUser($user); + + $db->loadDirectoriesAutomatic($app->directory); + if (isset($app->types[DT_STRUCT])) { + $tree = new Tree($db->getObjects($app->types[DT_STRUCT], true)); + } else { + $tree = null; + } + + if ($job['job'] instanceof ContainerAwareInterface) { + $job['job']->setContainer($this->container); + } + + /** @var AbstractCron $jobInstance */ + $jobInstance = $job['job']; + $res = $jobInstance->Run($app, $db, $tree, $matchedTime, $job['rule']); + + // !!! run code here !!! + $tmEnd = time(); + flock($fp, LOCK_UN); + fclose($fp); + + $cronStat = []; + $fp = @fopen($this->resultsFile, "r+"); + + if ($fp) { + do { + //nothing; + } while (!flock($fp, LOCK_EX)); + + fseek($fp, 0, SEEK_END); + $fsize = ftell($fp); + rewind($fp); + + if ($fsize > 0) { + $tStat = unserialize(fread($fp, $fsize)); + ftruncate($fp, 0); + rewind($fp); + + foreach ($tStat as $tHash => $vHash) { + if (isset($this->job2rule[$tHash])) { + $cronStat[$tHash] = $vHash; + } + } + } + + } else { + $fp = @fopen($this->resultsFile, "w"); + + if ($fp) { + do { + // nothing; + } while (!flock($fp, LOCK_EX)); + } + } + + $cronStat[$job['jobhash']] = ['start' => $tmStart, 'end' => $tmEnd, 'result' => $res]; + + fwrite($fp, serialize($cronStat)); + flock($fp, LOCK_UN); + fclose($fp); + } + + public function RunTasks(&$app, $matchedTime) + { + $t = localtime($matchedTime, true); + $pid = -1; + + foreach ($this->rules as $k) { + if ($this->isNotMatchTime($k['rule']->match, $t)) { + continue; + } + + if (($pid = pcntl_fork()) == 0) { + $this->runJob($k, $app, $matchedTime); //child code + exit(); //close child process + } + } + + if ($pid) { //parent code + while (pcntl_waitpid(-1, $status) > 0) { + //wanna some status ? + } + } + } + + public function isNotMatchTime($match, $t) + { + return !( + isset($match['min'][$t['tm_min']]) && + isset($match['hour'][$t['tm_hour']]) && + isset($match['mday'][$t['tm_mday']]) && + isset($match['mon'][$t['tm_mon'] + 1]) && + isset($match['wday'][$t['tm_wday']]) + ); + } + + private function _loadStat() + { + $fp = @fopen($this->resultsFile, 'r'); + + if ($fp) { + flock($fp, LOCK_EX); + + fseek($fp, 0, SEEK_END); + $fsize = ftell($fp); + rewind($fp); + $cronStat = unserialize(fread($fp, $fsize)); + + flock($fp, LOCK_UN); + fclose($fp); + + } else { + $cronStat = []; + } + + return $cronStat; + } + + public function getStat($job) + { + static $cronStat; + + if (is_null($cronStat)) { + $cronStat = $this->_loadStat(); + } + + if (isset($cronStat[$job['jobhash']])) { + $t = $cronStat[$job['jobhash']]; + } else { + $t = null; + } + + return $t; + } + + public function adminIndex() + { + $layout = $this->layout; + + $layout->setOneColumn(); + $layout->assign('INNER.0.1', 'Обновить'); + + $fields = [ + 'rule' => 'Правило', + 'name' => 'Название задачи', + 'time' => 'Начало/Окончание', + 'comment' => 'Примечание', + ]; + + $result = []; + + foreach ($this->rules as $k) { + $_ = []; + + $_['rule'] = $k['rule']->asString; + $_['name'] = $k['job']->name; + + + $t = $this->getStat($k); + + if (!is_null($t)) { + $diff = $t['end'] - $t['start']; + $dmin = (int)($diff / 60); + $dsec = $diff - $dmin * 60; + + $_['time'] = date($this->timeFormat, $t['start']) . '
' . date($this->timeFormat, $t['end']) . ' (' . $dmin . ' min ' . $dsec . ' sec)'; + $note = isset($t['result']['note']) ? htmlentities((string) $t['result']['note'], ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) : ''; + $_['comment'] = ($t['result']['status'] >= 0) ? '' . $note . '' : '' . $note . ''; + + } else { + $_['time'] = ' 
 '; + $_['comment'] = 'Еще не запускалась'; + } + + $result[] = $_; + } - $table = new PXAdminTableSimple($fields); - $table->setData($result); + $table = new PXAdminTableSimple($fields); + $table->setData($result); - $layout->assign('INNER.0.0', $table->html()); - } + $layout->assign('INNER.0.0', $table->html()); + } } - diff --git a/src/Module/CsvImportModule.php b/src/Module/CsvImportModule.php index 514e9dee..65e10e60 100644 --- a/src/Module/CsvImportModule.php +++ b/src/Module/CsvImportModule.php @@ -12,235 +12,234 @@ */ class CsvImportModule extends AbstractModule { - - public $htmlEsc; - public $settings; - - public function __construct($area, $settings) - { - parent::__construct($area, $settings); - - $this->settings = $settings; - $this->area = $area; - - $this->htmlEsc = [ - chr(145) => '‘', - chr(146) => '’', - chr(147) => '“', - chr(148) => '”', - chr(171) => '«', - chr(187) => '»', - chr(150) => '–', // – - chr(151) => '—', // — - chr(185) => '№', // russian number - chr(133) => '…', // … - chr(153) => '™' // ™ - ]; - } - - public function adminIndex() - { - $layout = $this->layout; - $request = $this->request; - - $layout->setOneColumn(); - $html = ''; - - switch ($request->GetVar('status')) { - case 'error': - $html .= $this->_UploadTable(); - $html .= '

Ошибка

'; - $html .= '

Импорт данных не состоялся.
'; - - switch ($request->GetVar('error')) { - case 'notupload': - $html .= 'Файл не закачан'; - break; - - case 'baddata': - case 'notdata': - $html .= 'Файл не содержит корректных данных'; - break; - } - - $html .= '

'; - break; - - case 'success': - $html .= $this->_Success(); - break; - - default: - $html .= $this->_UploadTable(); - break; - } - - $layout->Assign('INNER.0.0', $html); - } - - public function _Success() - { - $request = $this->request; - $app = $this->app; - - $html = '

Импорт данных «' . $app->types[$this->settings['datatype']]->title . '» успешно произведен

'; - $html .= '

В базу внесено ' . htmlspecialchars($request->GetVar('quantity'), ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) . ' объектов.

'; - return $html; - } - - public function adminAction() - { - $request = $this->request; - $app = $this->app; - $db = $this->db; - - $context = new ContextUrlGenerator(); - $context->setCurrentModule($this->area); - $generator = new UrlGenerator($context); - - $redirTo = $generator->getAdminGenerator()->indexUrl(); - - if (($filename = $this->UploadFile($request->GetUploadFile('file')))) { - $quantity = $this->ImportToDb($this->_GetCsvSource($filename), $app, $db, $request); - - if (is_numeric($quantity)) { - $redirTo .= '&status=success&quantity=' . $quantity; - } else { - $redirTo .= '&status=error&error=' . $quantity; - } - - } else { - $redirTo .= '&status=error&error=notupload'; - } - - return $redirTo; - } - - public function ImportToDB($csv) - { - $app = $this->app; - $db = $this->db; - - $objects = []; - $fields = []; - $parent = null; - - $format = $app->types[$this->settings['datatype']]; - - foreach ($this->settings['field'] as $f) { - [$k, $v] = explode('|', $f); - - if (trim($k) == 'parent') { - $v = $app->GetProperty(trim($v)); - $parent = $v; - } - - $fields[trim($k)] = trim($v); - } - - foreach ($csv as $ln => $line) { - if ($ln === 1 && isset($this->settings['skipfirst']) && $this->settings['skipfirst'] === 'true') { - continue; - } - - $valid = 0; - foreach ($line as $d) { - if (mb_strlen($d)) { - $valid++; - } - } - - if (!$valid) { - continue; - } - - $object = []; - - $this->constructObject($object, $fields, $line); - - $objects[] = $object; - } - - $db->transactionBegin(); - - $this->deleteOldObjects($db, $format, $parent); - - $db->addContentObjects($format, $objects); - $db->transactionCommit(); - - return count($objects); - } - - public function constructObject(&$object, $fields, $row) - { - foreach ($fields as $fk => $fv) { - if ($fk === 'parent') { - $object[$fk] = $fv; - } elseif (is_numeric($fv)) { - $object[$fk] = $row[$fv]; - } elseif (($fv === 'true' && ($fv = true)) || ($fv === 'false' && (($fv = false) || true))) { - $object[$fk] = $fv; - } else { - $object[$fk] = $fv; - } - } - } - - public function deleteOldObjects(&$db, &$format, $parent = null) - { - if (!is_null($parent)) { - $sql = 'DELETE FROM ' . $format->id . ' WHERE parent = ' . $parent . ';'; - } else { - $sql = 'DELETE FROM ' . $format->id; - } - - if ($db->ModifyingQuery($sql) == ERROR_DB_BADQUERY) { - FatalError("Ошибка в запросе" . $sql); - } - } - - public function UploadFile($file, $uploadDir = NULL) - { - $uploadDir = !is_null($uploadDir) ? $uploadDir : BASEPATH . '/tmp/csvimport'; - - if (!isset($file['name'])) { - return false; - } - - if (!preg_match('/\.(csv|txt)$/i', $file['name'])) { - return false; - } - - return $this->UploadFileFromUser($file, $uploadDir); - } - - public function UploadFileFromUser($file, $dir) - { - $dirO = new \NLDir($dir); - $dir = $dirO->name; - - MakeDirIfNotExists($dir); - - if ($file && $file != 'none' && $file['name'] && is_writable($dir)) { - $newFileName = $dir . '/' . _TranslitFilename(_stripBadFileChars($file['name'])); - - if (!@copy($file['tmp_name'], $newFileName)) { - FatalError('ERROR_IO_BADPERMISSIONS: ' . $dir); - } - - if (!@chmod($newFileName, 0664)) { - FatalError('ERROR_IO_BADPERMISSIONS: ' . $dir); - } - - return $newFileName; - } else { - return false; - } - } - - public function _UploadTable() - { - return <<settings = $settings; + $this->area = $area; + + $this->htmlEsc = [ + chr(145) => '‘', + chr(146) => '’', + chr(147) => '“', + chr(148) => '”', + chr(171) => '«', + chr(187) => '»', + chr(150) => '–', // – + chr(151) => '—', // — + chr(185) => '№', // russian number + chr(133) => '…', // … + chr(153) => '™' // ™ + ]; + } + + public function adminIndex() + { + $layout = $this->layout; + $request = $this->request; + + $layout->setOneColumn(); + $html = ''; + + switch ($request->GetVar('status')) { + case 'error': + $html .= $this->_UploadTable(); + $html .= '

Ошибка

'; + $html .= '

Импорт данных не состоялся.
'; + + switch ($request->GetVar('error')) { + case 'notupload': + $html .= 'Файл не закачан'; + break; + + case 'baddata': + case 'notdata': + $html .= 'Файл не содержит корректных данных'; + break; + } + + $html .= '

'; + break; + + case 'success': + $html .= $this->_Success(); + break; + + default: + $html .= $this->_UploadTable(); + break; + } + + $layout->Assign('INNER.0.0', $html); + } + + public function _Success() + { + $request = $this->request; + $app = $this->app; + + $html = '

Импорт данных «' . $app->types[$this->settings['datatype']]->title . '» успешно произведен

'; + $html .= '

В базу внесено ' . htmlspecialchars((string) $request->GetVar('quantity'), ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) . ' объектов.

'; + return $html; + } + + public function adminAction() + { + $request = $this->request; + $app = $this->app; + $db = $this->db; + + $context = new ContextUrlGenerator(); + $context->setCurrentModule($this->area); + $generator = new UrlGenerator($context); + + $redirTo = $generator->getAdminGenerator()->indexUrl(); + + if (($filename = $this->UploadFile($request->GetUploadFile('file')))) { + $quantity = $this->ImportToDb($this->_GetCsvSource($filename)); + + if (is_numeric($quantity)) { + $redirTo .= '&status=success&quantity=' . $quantity; + } else { + $redirTo .= '&status=error&error=' . $quantity; + } + + } else { + $redirTo .= '&status=error&error=notupload'; + } + + return $redirTo; + } + + public function ImportToDB($csv) + { + $app = $this->app; + $db = $this->db; + + $objects = []; + $fields = []; + $parent = null; + + $format = $app->types[$this->settings['datatype']]; + + foreach ($this->settings['field'] as $f) { + [$k, $v] = explode('|', (string) $f); + + if (trim($k) == 'parent') { + $v = $app->GetProperty(trim($v)); + $parent = $v; + } + + $fields[trim($k)] = trim((string) $v); + } + + foreach ($csv as $ln => $line) { + if ($ln === 1 && isset($this->settings['skipfirst']) && $this->settings['skipfirst'] === 'true') { + continue; + } + + $valid = 0; + foreach ($line as $d) { + if (mb_strlen((string) $d)) { + $valid++; + } + } + + if (!$valid) { + continue; + } + + $object = []; + + $this->constructObject($object, $fields, $line); + + $objects[] = $object; + } + + $db->transactionBegin(); + + $this->deleteOldObjects($db, $format, $parent); + + $db->addContentObjects($format, $objects); + $db->transactionCommit(); + + return count($objects); + } + + public function constructObject(&$object, $fields, $row) + { + foreach ($fields as $fk => $fv) { + if ($fk === 'parent') { + $object[$fk] = $fv; + } elseif (is_numeric($fv)) { + $object[$fk] = $row[$fv]; + } elseif (($fv === 'true' && ($fv = true)) || ($fv === 'false' && (($fv = false) || true))) { + $object[$fk] = $fv; + } else { + $object[$fk] = $fv; + } + } + } + + public function deleteOldObjects(&$db, &$format, $parent = null) + { + if (!is_null($parent)) { + $sql = 'DELETE FROM ' . $format->id . ' WHERE parent = ' . $parent . ';'; + } else { + $sql = 'DELETE FROM ' . $format->id; + } + + if ($db->ModifyingQuery($sql) == ERROR_DB_BADQUERY) { + FatalError("Ошибка в запросе" . $sql); + } + } + + public function UploadFile($file, $uploadDir = null) + { + $uploadDir = !is_null($uploadDir) ? $uploadDir : BASEPATH . '/tmp/csvimport'; + + if (!isset($file['name'])) { + return false; + } + + if (!preg_match('/\.(csv|txt)$/i', (string) $file['name'])) { + return false; + } + + return $this->UploadFileFromUser($file, $uploadDir); + } + + public function UploadFileFromUser($file, $dir) + { + $dirO = new \NLDir($dir); + $dir = $dirO->name; + + MakeDirIfNotExists($dir); + + if ($file && $file != 'none' && $file['name'] && is_writable($dir)) { + $newFileName = $dir . '/' . _TranslitFilename(_stripBadFileChars($file['name'])); + + if (!@copy($file['tmp_name'], $newFileName)) { + FatalError('ERROR_IO_BADPERMISSIONS: ' . $dir); + } + + if (!@chmod($newFileName, 0664)) { + FatalError('ERROR_IO_BADPERMISSIONS: ' . $dir); + } + + return $newFileName; + } else { + return false; + } + } + + public function _UploadTable() + { + return <<Импорт данных из CSV файла
@@ -249,43 +248,43 @@ public function _UploadTable()
HTML; - } - - public function _GetCsvSource($filename, $length = NULL) - { - if (!is_file($filename) || !is_readable($filename)) { - return FALSE; - } - - $row = 1; - $maxLen = 0; - $handle = fopen($filename, "r"); - $result = []; - - while (($data = fgetcsv($handle, 4096, ";")) !== FALSE && (!$length || $row <= $length)) { - $num = count($data); - for ($c = 0; $c < $num; $c++) { - $result[$row][$c] = iconv(CHARSET_WINDOWS, CHARSET_UTF8, $data[$c]); - } - $maxLen = max($maxLen, $num); - $row++; - } - - fclose($handle); - - foreach ($result as $k => $v) { - if (count($v) < $maxLen) { - $result[$k] = array_pad($result[$k], $maxLen, NULL); - } - } - - return $result; - } - - // method for safe access - public function getParsedCsv($filename, $length = null) - { - return $this->_GetCsvSource($filename, $length); - } + } + + public function _GetCsvSource($filename, $length = null) + { + if (!is_file($filename) || !is_readable($filename)) { + return false; + } + + $row = 1; + $maxLen = 0; + $handle = fopen($filename, "r"); + $result = []; + + while (($data = fgetcsv($handle, 4096, ";")) !== false && (!$length || $row <= $length)) { + $num = count($data); + for ($c = 0; $c < $num; $c++) { + $result[$row][$c] = iconv(CHARSET_WINDOWS, CHARSET_UTF8, (string) $data[$c]); + } + $maxLen = max($maxLen, $num); + $row++; + } + + fclose($handle); + + foreach ($result as $k => $v) { + if (count($v) < $maxLen) { + $result[$k] = array_pad($result[$k], $maxLen, null); + } + } + + return $result; + } + + // method for safe access + public function getParsedCsv($filename, $length = null) + { + return $this->_GetCsvSource($filename, $length); + } } diff --git a/src/Module/FileModule.php b/src/Module/FileModule.php index 98d8cbe4..d1c2b3c4 100644 --- a/src/Module/FileModule.php +++ b/src/Module/FileModule.php @@ -12,121 +12,120 @@ */ class FileModule extends AbstractModule { - - public $settings; - public $area; - protected $protected; - - public function __construct($area, $settings) - { - parent::__construct($area, $settings); - - $this->area = $area; - $this->settings = []; - - $this->_parseSettings($settings); - } - - public function _parseSettings($settings) - { - if (!isset($settings['dir'])) { - return; - } - - $openDirs = $settings['dir']; - $this->protected = $settings['protected'] ?? []; - - if (is_string($openDirs)) { - $openDirs = [$openDirs]; - } - - foreach ($openDirs as $element) { - $tmp = explode('=', $element); - $d = $tmp[0]; - $d = str_replace('\\', '/', $d); - - $this->settings[trim($d)] = trim($tmp[1]); - } - } - - public function error() - { - if (sizeof($this->settings)) { - return false; - } - - $this->layout->setOneColumn(); - $this->layout->assign('INNER.0.0', '
Нет ни одного каталога доступного для просмотра!!!
'); - return true; - } - - public function adminIndex() - { - if ($this->error()) { - return; - } - - $this->_setInnerLayout(); - - $tabs = [ - [ - 'cur' => 'ldir', - 'oth' => 'rdir', - 'cell' => '0', - 'side' => 'l', - ], - - [ - 'cur' => 'rdir', - 'oth' => 'ldir', - 'cell' => '1', - 'side' => 'r', - ], - ]; - - $lDir = $this->request->getVar('ldir'); - $rDir = $this->request->getVar('rdir'); - - foreach ($tabs as $tab) { - $curDir = $tab['side'] == 'l' ? $lDir : $rDir; - $othDir = $tab['side'] != 'l' ? $lDir : $rDir; - - $href = '?area=' . $this->area . '&' . $tab['oth'] . '=' . rawurlencode($othDir); - - $listingClass = $curDir ? 'PXFileListing' : 'PXFileListingRoots'; - $listing = new $listingClass($this->settings); - - $listing->setDestination($othDir); - $listing->getList($curDir); - $listing->setDecorator(new \PXHTMLFileListing($href, $tab['side'], function ($alias, $f) { - $url = $alias; - - $catalog = rtrim($f->catalog, '/'); - foreach ($this->protected as $protected) { - if (strncmp($catalog, $protected, mb_strlen($protected)) === 0) { - $mimeType = mime_content_type($f->path); - $url = 'action.phtml?area=file&url=' . $alias . '&type=' . $mimeType . '&action=protected'; - break; - } - } - - return $url; - })); - - $this->layout->assign('INNER.' . $tab['cell'] . '.0', $listing->html()); - - if ($listing->writable()) { - $this->layout->assign('INNER.' . $tab['cell'] . '.1', $this->_uploadForm($this->area, $lDir, $rDir, $tab['side'], FALSE, $this->area)); - } - - unset($listing); - } - } - - public function _uploadForm($area, $ldir, $rdir, $side = 'l', $name = FALSE, $outside = NULL) - { - $_ = - <<area = $area; + $this->settings = []; + + $this->_parseSettings($settings); + } + + public function _parseSettings($settings) + { + if (!isset($settings['dir'])) { + return; + } + + $openDirs = $settings['dir']; + $this->protected = $settings['protected'] ?? []; + + if (is_string($openDirs)) { + $openDirs = [$openDirs]; + } + + foreach ($openDirs as $element) { + $tmp = explode('=', (string) $element); + $d = $tmp[0]; + $d = str_replace('\\', '/', $d); + + $this->settings[trim($d)] = trim($tmp[1]); + } + } + + public function error() + { + if (sizeof($this->settings)) { + return false; + } + + $this->layout->setOneColumn(); + $this->layout->assign('INNER.0.0', '
Нет ни одного каталога доступного для просмотра!!!
'); + return true; + } + + public function adminIndex() + { + if ($this->error()) { + return; + } + + $this->_setInnerLayout(); + + $tabs = [ + [ + 'cur' => 'ldir', + 'oth' => 'rdir', + 'cell' => '0', + 'side' => 'l', + ], + + [ + 'cur' => 'rdir', + 'oth' => 'ldir', + 'cell' => '1', + 'side' => 'r', + ], + ]; + + $lDir = $this->request->getVar('ldir'); + $rDir = $this->request->getVar('rdir'); + + foreach ($tabs as $tab) { + $curDir = $tab['side'] == 'l' ? $lDir : $rDir; + $othDir = $tab['side'] != 'l' ? $lDir : $rDir; + + $href = '?area=' . $this->area . '&' . $tab['oth'] . '=' . rawurlencode((string) $othDir); + + $listingClass = $curDir ? 'PXFileListing' : 'PXFileListingRoots'; + $listing = new $listingClass($this->settings); + + $listing->setDestination($othDir); + $listing->getList($curDir); + $listing->setDecorator(new \PXHTMLFileListing($href, $tab['side'], function ($alias, $f) { + $url = $alias; + + $catalog = rtrim((string) $f->catalog, '/'); + foreach ($this->protected as $protected) { + if (strncmp($catalog, $protected, mb_strlen($protected)) === 0) { + $mimeType = mime_content_type($f->path); + $url = 'action.phtml?area=file&url=' . $alias . '&type=' . $mimeType . '&action=protected'; + break; + } + } + + return $url; + })); + + $this->layout->assign('INNER.' . $tab['cell'] . '.0', $listing->html()); + + if ($listing->writable()) { + $this->layout->assign('INNER.' . $tab['cell'] . '.1', $this->_uploadForm($this->area, $lDir, $rDir, $tab['side'], false, $this->area)); + } + + unset($listing); + } + } + + public function _uploadForm($area, $ldir, $rdir, $side = 'l', $name = false, $outside = null) + { + $_ = + << @@ -148,29 +147,29 @@ public function _uploadForm($area, $ldir, $rdir, $side = 'l', $name = FALSE, $ou HTML; - $replaces = [ - 'SIDE' => $side, - 'DIR' => ($side == 'l' ? urlencode($ldir) : urlencode($rdir)), - 'HREF' => '?area=' . $this->area . '&ldir=' . urlencode($ldir) . '&rdir=' . urlencode($rdir) . ($name ? '&name=' . $name : ''), - 'AREA' => $this->area, - 'OUTSIDE' => $outside, - 'MDIR' => ($side == 'l' ? $ldir : $rdir), - 'LDIR' => $ldir, - 'RDIR' => $rdir, - 'NAME' => $name, - ]; - - foreach ($replaces as $label => $value) { - $_ = str_replace('%' . $label . '%', $value, $_); - } - - return $_; - } - - public function _setInnerLayout() - { // FIXME!!! - $_html = - << $side, + 'DIR' => ($side == 'l' ? urlencode((string) $ldir) : urlencode((string) $rdir)), + 'HREF' => '?area=' . $this->area . '&ldir=' . urlencode((string) $ldir) . '&rdir=' . urlencode((string) $rdir) . ($name ? '&name=' . $name : ''), + 'AREA' => $this->area, + 'OUTSIDE' => $outside, + 'MDIR' => ($side == 'l' ? $ldir : $rdir), + 'LDIR' => $ldir, + 'RDIR' => $rdir, + 'NAME' => $name, + ]; + + foreach ($replaces as $label => $value) { + $_ = str_replace('%' . $label . '%', $value, $_); + } + + return $_; + } + + public function _setInnerLayout() + { // FIXME!!! + $_html = + <<
HTML; - $this->layout->assign('OUTER.MAINAREA', $_html); - } - - public function adminPopup() - { - $this->layout->setGetVarToSave('id', $this->request->GetId()); - $this->layout->setGetVarToSave('format', $this->request->GetFormat()); - $this->layout->setGetVarToSave('action', $this->request->GetAction()); - - $this->_initRequestVars(); - - $this->layout->setOuterForm('action.phtml', 'POST', 'multipart/form-data'); - switch ($this->request->getVar('action')) { - case 'edit': - $html = '
' . $this->editFileForm() . '
'; - break; - - //FIXME: UNUSED? - /*case 'link': - $rName = $this->request->GetVar('name'); - - $curDir = $this->request->GetVar('ldir'); - $othDir = null; - - $href = '?area='.$this->area.'&action=link&name='.$rName.'&rdir='; - - $listingClass = $curDir ? 'PXFileListing' : 'PXFileListingRoots'; - $listing = new $listingClass($this->settings); - - $listing->setDestination($othDir); - $listing->getList($curDir); - $listing->setDecorator(new \PXHTMLFileListingLink($href, 'l', $rName)); - - $this->layout->assign('OUTER.LEFTCONTROLS', ''); - - $html = '
'.$listing->html().'
'; - break;*/ - - default: - $html = '
Действие не определено
'; - break; - } - - return $html; - } - - public function editFileForm() - { - $filePath = $this->catalog . $this->mFile; - - if (is_file($filePath) && is_writable($filePath) && !is_binary($filePath)) { - $object = []; - $form = new \PXAdminForm($object, null); - - $this->layout->assign('OUTER.TITLE', 'Редактирование файла «' . $this->mFile . '»'); - $form->leftControls(); - $form->rightControls(); - - return $form->editTextFileForm($this->mFile, $this->catalog); - } else { - return 'Редактирование файлов подобного формата еще не реализовано'; - } - } - - public function _initRequestVars() - { - $rSide = $this->request->getVar('side'); - $lDir = $this->request->getVar('ldir'); - $rDir = $this->request->getVar('rdir'); - - $outSide = $this->request->getVar('outside'); - - $this->catObject = new \PXFileListing($this->settings); - - $this->redir = (!empty($outSide) && $outSide !== $this->area ? 'popup.phtml?action=' . $outSide . '&name=' . $this->request->getVar('name') . '&' : './?'); - $this->redir .= 'area=' . $this->area . '&ldir=' . $lDir . '&rdir=' . $rDir; - - $catalog = ($rSide != 'r' ? $lDir : $rDir); - $this->catalog = $catalog && $this->catObject->isValid($catalog) ? $this->fullPath($catalog) . '/' : null; - - $destination = ($rSide == 'r' ? $lDir : $rDir); - $this->destination = $destination && $this->catObject->isValid($destination) ? $this->fullPath($destination) . '/' : null; - - $this->mFile = _stripBadFileChars($this->request->getVar('mfile')); - $this->nFile = $this->_escapeFileName($this->request->getVar('nfile')); - } - - public function adminAction() - { - $this->_initRequestVars(); - - switch ($this->request->getVar('action')) { - case 'createdir': - $this->withArgs([$this->catalog], '_aCreateDir'); - break; - case 'copy': - $this->withArgs([$this->catalog, $this->destination, $this->mFile], '_aCopy'); - break; - case 'move': - $this->withArgs([$this->catalog, $this->destination, $this->mFile], '_aMove'); - break; - case 'rename': - $this->withArgs([$this->catalog, $this->mFile, $this->nFile], '_aRename'); - break; - case 'delete': - $this->withArgs([$this->catalog, $this->mFile], '_aDelete'); - break; - case 'edit': - $this->withArgs([$this->mFile], '_aEdit'); - break; - case 'unzip': - $this->withArgs([$this->catalog, $this->mFile], '_aUnZip'); - break; - case 'upload': - $this->withArgs([$this->catalog], '_aUpload'); - break; - case 'protected': - $this->_aProtectedAccess(); - break; - } - - return $this->redir; - } - - protected function fullPath($path) - { - return BASEPATH . DIRECTORY_SEPARATOR . $path; - } - - protected function withArgs($fields, $action) - { - count($fields) == count(array_filter($fields)) && $this->{$action}(); - } - - public function _escapeFileName($name) - { - return $name = _TranslitFilename(_stripBadFileChars($name)); - } - - # Actions - - /** - * Для того, чтобы работали защищенные ссылки на файлы, нужно добавить соответствующие internal location в nginx - */ - public function _aProtectedAccess() - { - $url = $this->request->getVar('url'); - $type = $this->request->getVar('type'); - - $this->response->addHeader('X-Accel-Redirect', $url) - ->setContentType($type) - ->downloadFile(basename($url)) - ->send(); - } - - public function _aEdit() - { - $this->redir = NULL; - - if (!$this->catObject->isValid($mdir = $this->request->getVar('mdir'))) { - return; - } - - $mdir = $this->fullPath($mdir); - $source = $this->request->getVar('filesource'); - $filePath = $mdir . $this->mFile; - - if (is_file($filePath) && is_writable($filePath) && !is_binary($filePath)) { - $fp = fopen($filePath, 'w'); - fwrite($fp, $source); - fclose($fp); - } - } - - public function _aDelete() - { - $filePath = $this->catalog . $this->mFile; - - if (!is_writable($this->catalog)) { - return; - } - - switch (true) { - case is_file($filePath): - unlink($filePath); - break; - - case is_dir($filePath): - $d = new \NLDir($filePath); - $d->Delete(); - break; - } - } - - public function _aCopy() - { - $entry = $this->mFile; - - if (file_exists($this->catalog . $entry) && is_writable($this->destination)) { - copyr($this->catalog . $entry, $this->destination . $entry); - } - } - - public function _aMove() - { - $entry = $this->mFile; - - if (file_exists($this->catalog . $entry) && is_writable($this->destination) && is_writable($this->catalog)) { - copyr($this->catalog . $entry, $this->destination . $entry); - - $this->_aDelete(); - } - } - - public function _aCreateDir() - { - $ndir = $this->_escapeFileName($this->request->getVar('ndir')); - - if (is_dir($this->catalog) && is_writable($this->catalog) && !is_dir($this->catalog . $ndir)) { - MakeDirIfNotExists($this->catalog . $ndir); - } - } - - public function _aRename() - { - $oldFile = $this->mFile; - $newFile = $this->nFile; - - if ((is_file($this->catalog . $oldFile) || is_dir($this->catalog . $oldFile)) && is_writable($this->catalog) && (!is_file($this->catalog . $newFile) && !is_dir($this->catalog . $newFile))) { - rename($this->catalog . $oldFile, $this->catalog . $newFile); - } - } - - public function _aUnZip() - { - if (is_file($this->catalog . $this->mFile) && is_writable($this->catalog)) { - system(sprintf("unzip -oq %s -d %s", escapeshellarg($this->catalog . $this->mFile), escapeshellarg($this->catalog))); - } - } - - public function _aUpload() - { - if ($this->request->GetUploadFile('uploadfile')) { - $this->uploadFileFromUser($this->request->GetUploadFile('uploadfile')); - } elseif ($this->request->GetVar('uploadfileurl')) { - $this->uploadFileFromWeb($this->request->GetVar('uploadfileurl')); - } - } - - public function uploadFileFromUser($file) - { - if ($file && $file != 'none' && $file['name'] && is_writable($this->catalog)) { - $filename = _TranslitFilename(_stripBadFileChars(stripslashes($file['name']))); - if (!@copy($file['tmp_name'], $this->catalog . $filename)) { - FatalError('Не удалось скопировать файл ' . $file['tmp_name'] . ' в каталог ' . $this->catalog); - } - - if (!@chmod($this->catalog . $filename, 0664)) { - FatalError('Не удалось изменить права доступа файлу ' . $this->catalog . $filename); - } - } - } - - public function uploadFileFromWeb($url) - { - if ($url && mb_strlen($url) && preg_match('#^https?://#', $url) && is_writable($this->catalog)) { - if ($fp = @fopen($url, 'r')) { - $fileSource = ''; - - while ($r = fread($fp, 2048)) { - $fileSource .= $r; - } - - fclose($fp); - - if (mb_strlen($fileSource)) { - $filename = tempnam($this->catalog, 'file'); - - if ($fp1 = @fopen($filename, 'w')) { - @fwrite($fp1, $fileSource); - @fclose($fp1); - } else { - FatalError('Не удалось открыть/создать файл ' . $filename); - } - - if (!@chmod($filename, 0664)) { - FatalError('Не удалось изменить права доступа файлу ' . $filename); - } - - if (preg_match_all("#/([\w\-]+\.\w{1,5})(\?.*)?$#", $url, $matches, PREG_SET_ORDER) && isset($matches[0][0])) { - $matches[0][0] = _TranslitFilename($matches[0][0]); - rename($filename, $this->catalog . $matches[0][0]); - $filename = $this->catalog . $matches[0][0]; - } - - $map = [ - '1' => 'gif', - '2' => 'jpg', - '3' => 'png', - '6' => 'bmp', - ]; - - $fileAttr = getimagesize($filename); - - if (isset($map[$fileAttr[2]])) { - $subname = str_replace($this->catalog, '', $filename); - if (mb_strpos($subname, '.')) { - $subname = mb_substr($subname, 0, mb_strpos($subname, '.')); - rename($filename, $this->catalog . $subname . "." . $map[$fileAttr[2]]); - $filename = $this->catalog . $subname . "." . $map[$fileAttr[2]]; - } else { - rename($filename, $filename . "." . $map[$fileAttr[2]]); - $filename = $filename . "." . $map[$fileAttr[2]]; - } - } - } - } - } - } + $this->layout->assign('OUTER.MAINAREA', $_html); + } + + public function adminPopup() + { + $this->layout->setGetVarToSave('id', $this->request->GetId()); + $this->layout->setGetVarToSave('format', $this->request->GetFormat()); + $this->layout->setGetVarToSave('action', $this->request->GetAction()); + + $this->_initRequestVars(); + + $this->layout->setOuterForm('action.phtml', 'POST', 'multipart/form-data'); + $html = match ($this->request->getVar('action')) { + 'edit' => '
' . $this->editFileForm() . '
', + default => '
Действие не определено
', + }; + + return $html; + } + + public function editFileForm() + { + $filePath = $this->catalog . $this->mFile; + + if (is_file($filePath) && is_writable($filePath) && !is_binary($filePath)) { + $object = []; + $form = new \PXAdminForm($object, null); + + $this->layout->assign('OUTER.TITLE', 'Редактирование файла «' . $this->mFile . '»'); + $form->leftControls(); + $form->rightControls(); + + return $form->editTextFileForm($this->mFile, $this->catalog); + } else { + return 'Редактирование файлов подобного формата еще не реализовано'; + } + } + + public function _initRequestVars() + { + $rSide = $this->request->getVar('side'); + $lDir = $this->request->getVar('ldir'); + $rDir = $this->request->getVar('rdir'); + + $outSide = $this->request->getVar('outside'); + + $this->catObject = new \PXFileListing($this->settings); + + $this->redir = (!empty($outSide) && $outSide !== $this->area ? 'popup.phtml?action=' . $outSide . '&name=' . $this->request->getVar('name') . '&' : './?'); + $this->redir .= 'area=' . $this->area . '&ldir=' . $lDir . '&rdir=' . $rDir; + + $catalog = ($rSide != 'r' ? $lDir : $rDir); + $this->catalog = $catalog && $this->catObject->isValid($catalog) ? $this->fullPath($catalog) . '/' : null; + + $destination = ($rSide == 'r' ? $lDir : $rDir); + $this->destination = $destination && $this->catObject->isValid($destination) ? $this->fullPath($destination) . '/' : null; + + $this->mFile = _stripBadFileChars($this->request->getVar('mfile')); + $this->nFile = $this->_escapeFileName($this->request->getVar('nfile')); + } + + public function adminAction() + { + $this->_initRequestVars(); + + match ($this->request->getVar('action')) { + 'createdir' => $this->withArgs([$this->catalog], '_aCreateDir'), + 'copy' => $this->withArgs([$this->catalog, $this->destination, $this->mFile], '_aCopy'), + 'move' => $this->withArgs([$this->catalog, $this->destination, $this->mFile], '_aMove'), + 'rename' => $this->withArgs([$this->catalog, $this->mFile, $this->nFile], '_aRename'), + 'delete' => $this->withArgs([$this->catalog, $this->mFile], '_aDelete'), + 'edit' => $this->withArgs([$this->mFile], '_aEdit'), + 'unzip' => $this->withArgs([$this->catalog, $this->mFile], '_aUnZip'), + 'upload' => $this->withArgs([$this->catalog], '_aUpload'), + 'protected' => $this->_aProtectedAccess(), + default => $this->redir, + }; + + return $this->redir; + } + + protected function fullPath($path) + { + return BASEPATH . DIRECTORY_SEPARATOR . $path; + } + + protected function withArgs($fields, $action) + { + (is_countable($fields) ? count($fields) : 0) == count((array) array_filter($fields)) && $this->{$action}(); + } + + public function _escapeFileName($name) + { + return $name = _TranslitFilename(_stripBadFileChars($name)); + } + + # Actions + + /** + * Для того, чтобы работали защищенные ссылки на файлы, нужно добавить соответствующие internal location в nginx + */ + public function _aProtectedAccess() + { + $url = $this->request->getVar('url'); + $type = $this->request->getVar('type'); + + $this->response->addHeader('X-Accel-Redirect', $url) + ->setContentType($type) + ->downloadFile(basename((string) $url)) + ->send(); + } + + public function _aEdit() + { + $this->redir = null; + + if (!$this->catObject->isValid($mdir = $this->request->getVar('mdir'))) { + return; + } + + $mdir = $this->fullPath($mdir); + $source = $this->request->getVar('filesource'); + $filePath = $mdir . $this->mFile; + + if (is_file($filePath) && is_writable($filePath) && !is_binary($filePath)) { + $fp = fopen($filePath, 'w'); + fwrite($fp, (string) $source); + fclose($fp); + } + } + + public function _aDelete() + { + $filePath = $this->catalog . $this->mFile; + + if (!is_writable($this->catalog)) { + return; + } + + switch (true) { + case is_file($filePath): + unlink($filePath); + break; + + case is_dir($filePath): + $d = new \NLDir($filePath); + $d->Delete(); + break; + } + } + + public function _aCopy() + { + $entry = $this->mFile; + + if (file_exists($this->catalog . $entry) && is_writable($this->destination)) { + copyr($this->catalog . $entry, $this->destination . $entry); + } + } + + public function _aMove() + { + $entry = $this->mFile; + + if (file_exists($this->catalog . $entry) && is_writable($this->destination) && is_writable($this->catalog)) { + copyr($this->catalog . $entry, $this->destination . $entry); + + $this->_aDelete(); + } + } + + public function _aCreateDir() + { + $ndir = $this->_escapeFileName($this->request->getVar('ndir')); + + if (is_dir($this->catalog) && is_writable($this->catalog) && !is_dir($this->catalog . $ndir)) { + MakeDirIfNotExists($this->catalog . $ndir); + } + } + + public function _aRename() + { + $oldFile = $this->mFile; + $newFile = $this->nFile; + + if ((is_file($this->catalog . $oldFile) || is_dir($this->catalog . $oldFile)) && is_writable($this->catalog) && (!is_file($this->catalog . $newFile) && !is_dir($this->catalog . $newFile))) { + rename($this->catalog . $oldFile, $this->catalog . $newFile); + } + } + + public function _aUnZip() + { + if (is_file($this->catalog . $this->mFile) && is_writable($this->catalog)) { + system(sprintf("unzip -oq %s -d %s", escapeshellarg($this->catalog . $this->mFile), escapeshellarg((string) $this->catalog))); + } + } + + public function _aUpload() + { + if ($this->request->GetUploadFile('uploadfile')) { + $this->uploadFileFromUser($this->request->GetUploadFile('uploadfile')); + } elseif ($this->request->GetVar('uploadfileurl')) { + $this->uploadFileFromWeb($this->request->GetVar('uploadfileurl')); + } + } + + public function uploadFileFromUser($file) + { + if ($file && $file != 'none' && $file['name'] && is_writable($this->catalog)) { + $filename = _TranslitFilename(_stripBadFileChars(stripslashes((string) $file['name']))); + if (!@copy($file['tmp_name'], $this->catalog . $filename)) { + FatalError('Не удалось скопировать файл ' . $file['tmp_name'] . ' в каталог ' . $this->catalog); + } + + if (!@chmod($this->catalog . $filename, 0664)) { + FatalError('Не удалось изменить права доступа файлу ' . $this->catalog . $filename); + } + } + } + + public function uploadFileFromWeb($url) + { + if ($url && mb_strlen((string) $url) && preg_match('#^https?://#', (string) $url) && is_writable($this->catalog)) { + if ($fp = @fopen($url, 'r')) { + $fileSource = ''; + + while ($r = fread($fp, 2048)) { + $fileSource .= $r; + } + + fclose($fp); + + if (mb_strlen($fileSource)) { + $filename = tempnam($this->catalog, 'file'); + + if ($fp1 = @fopen($filename, 'w')) { + @fwrite($fp1, $fileSource); + @fclose($fp1); + } else { + FatalError('Не удалось открыть/создать файл ' . $filename); + } + + if (!@chmod($filename, 0664)) { + FatalError('Не удалось изменить права доступа файлу ' . $filename); + } + + if (preg_match_all("#/([\w\-]+\.\w{1,5})(\?.*)?$#", (string) $url, $matches, PREG_SET_ORDER) && isset($matches[0][0])) { + $matches[0][0] = _TranslitFilename($matches[0][0]); + rename($filename, $this->catalog . $matches[0][0]); + $filename = $this->catalog . $matches[0][0]; + } + + $map = [ + '1' => 'gif', + '2' => 'jpg', + '3' => 'png', + '6' => 'bmp', + ]; + + $fileAttr = getimagesize($filename); + + if (isset($map[$fileAttr[2]])) { + $subname = str_replace($this->catalog, '', $filename); + if (mb_strpos($subname, '.')) { + $subname = mb_substr($subname, 0, mb_strpos($subname, '.')); + rename($filename, $this->catalog . $subname . "." . $map[$fileAttr[2]]); + $filename = $this->catalog . $subname . "." . $map[$fileAttr[2]]; + } else { + rename($filename, $filename . "." . $map[$fileAttr[2]]); + $filename = $filename . "." . $map[$fileAttr[2]]; + } + } + } + } + } + } } diff --git a/src/Module/MaclModule.php b/src/Module/MaclModule.php index 8ef03845..afb29517 100644 --- a/src/Module/MaclModule.php +++ b/src/Module/MaclModule.php @@ -9,68 +9,61 @@ */ class MaclModule extends AclModule { - - public $what; - public $access; - public $objectRule = 'module'; - public $moduleName = 'macl'; - public $aclObjectTitle = 'Модуль'; - - public function getAvailableActions() - { - $actions = ['default' => AbstractModule::getAclModuleActions()]; - - foreach ($this->app->modules as $name => $module) { - $actions[$module->getName()] = call_user_func([$module->getClass(), 'getAclModuleActions']); - } - - return $actions; - } - - public function _getTypes() - { - $types = []; - - foreach ($this->app->modules as $module) { - if ($module->getDescription() == '' || $module->getDescription() == \PXModuleDescription::EMPTY_DESCRIPTION) { - $types[$module->getName()] = $module->getName(); - } else { - $types[$module->getName()] = $module->getDescription(); - } - } - - $types[null] = '-- любой --'; - - return $types; - } - - public function getWhatDict() - { - $defaultActionList = $this->what['default']; - $allActions = $this->getAvailableActions(); - - foreach ($allActions as $moduleName => $moduleActions) { - foreach ($moduleActions as $actionName => $actionTitle) { - if (!isset($defaultActionList[$actionName])) { - $defaultActionList[$actionName] = $actionTitle; - } - } - } - - return $defaultActionList; - } - - public function adminJson() - { - $current = trim($this->request->getVar('currentModule')); - - if (isset($this->what[$current])) { - $result = $this->what[$current]; - } else { - $result = $this->what['default']; - } - - return $result; - } + public $what; + public $access; + public $objectRule = 'module'; + public $moduleName = 'macl'; + public $aclObjectTitle = 'Модуль'; + + public function getAvailableActions() + { + $actions = ['default' => AbstractModule::getAclModuleActions()]; + + foreach ($this->app->modules as $name => $module) { + $actions[$module->getName()] = call_user_func([$module->getClass(), 'getAclModuleActions']); + } + + return $actions; + } + + public function _getTypes() + { + $types = []; + + foreach ($this->app->modules as $module) { + if ($module->getDescription() == '' || $module->getDescription() == \PXModuleDescription::EMPTY_DESCRIPTION) { + $types[$module->getName()] = $module->getName(); + } else { + $types[$module->getName()] = $module->getDescription(); + } + } + + $types[null] = '-- любой --'; + + return $types; + } + + public function getWhatDict() + { + $defaultActionList = $this->what['default']; + $allActions = $this->getAvailableActions(); + + foreach ($allActions as $moduleName => $moduleActions) { + foreach ($moduleActions as $actionName => $actionTitle) { + if (!isset($defaultActionList[$actionName])) { + $defaultActionList[$actionName] = $actionTitle; + } + } + } + + return $defaultActionList; + } + + public function adminJson() + { + $current = trim((string) $this->request->getVar('currentModule')); + + return $this->what[$current] ?? $this->what['default']; + } } diff --git a/src/Module/MainModule.php b/src/Module/MainModule.php index 450eb4e8..847b1b8e 100644 --- a/src/Module/MainModule.php +++ b/src/Module/MainModule.php @@ -11,371 +11,368 @@ */ class MainModule extends AbstractModule { + private $rootFormatId; + private readonly array $objectsLoadMode; + private ?\PXTypeDescription $rootFormat = null; + + public function __construct($area, $settings) + { + parent::__construct($area, $settings); + + $this->rootFormatId = $settings['rootFormat']; + + $this->objectsLoadMode = [ + PP_CHILDREN_FETCH_NONE => 'none', + PP_CHILDREN_FETCH_SELECTED => 'selected', + PP_CHILDREN_FETCH_ALL => 'all', + PP_CHILDREN_FETCH_PAGED => 'paged', + ]; + } + + public function userIndex() + { + // Bad path + if ($this->request->IsBadPath()) { + $this->response->notFound(); + return; + } + + // Get path + $urlPath = $this->request->getHostAndDir(); + $urlFile = $this->request->getFile(); + $urlPart = $this->request->getPart(); + + $this->tree->setFormat($this->rootFormatId); + $this->tree->setAliases($this->_parseAliasesConfig()); + + // Loading struct tree + $this->tree->load($urlPath); + $this->tree->getLinks(); + + // Loading content objects + $this->loadObjects($this->tree, $urlFile, $this->objects); + $this->objects->getLinks(); + + // Loading subcontent objects + $this->loadObjects($this->objects, $urlPart, $this->subObjects); + $this->subObjects->getLinks(); + + $this->check404error($urlFile, $urlPart); + } + + public function check404error($urlFile, $urlPart) + { + // Определение кода ошибки + if ( + !$this->tree->hasCurrent() || + !($this->request->isIndexFile($urlFile) || $this->objects->hasCurrent()) || + !($this->request->isIndexFile($urlPart) || $this->subObjects->hasCurrent()) + ) { + $this->response->notFound(); + } else { + $this->response->setOk(); + } + } + + protected function _parseAliasesConfig() + { + if (!isset($this->settings['domainAlias'])) { + $this->settings['domainAlias'] = []; + return []; + } + + $config = $this->settings['domainAlias']; + if (is_string($config)) { + $config = explode("\n", $config); + } + + $aliases = []; + foreach ($config as $row) { + [$host, $alias] = preg_split('/\s*=\s*/', trim((string) $row)); + $aliases[$host] = $alias; + } + + return $aliases; + } + + private function loadObjects($object, $pathName, $fillObj) + { + if (!$object->hasCurrent()) { + return; + } + + $allowed = $object->getAllowedChilds(); + + if (!(is_countable($allowed) ? count($allowed) : 0)) { + return; + } + + foreach ($allowed as $type => $behaviour) { + $format = \PXRegistry::getTypes($type); + + if (!is_object($format)) { + continue; + } + + $loadingMethod = 'load' . ucfirst((string) $this->objectsLoadMode[$behaviour]) . 'Objects'; + $objsArray = $this->$loadingMethod($object, $format, $pathName); + + $fillObj->add($type, $objsArray); + $fillObj->findCurrent($type, $pathName); + } + } + + private function loadAllObjects($object, $format, $pathName) + { + $objsArray = []; + + $objsArray = $this->_loadContent($format, true, 'parent', $object->currentId); + + return $objsArray; + } + + private function loadNoneObjects($object, $format, $pathName) + { + return []; + } + + private function loadPagedObjects($object, $format, $pathName) + { + $objsArray = []; - private $rootFormatId; - private $objectsLoadMode; - private $rootFormat; - - public function __construct($area, $settings) - { - parent::__construct($area, $settings); - - $this->rootFormatId = $settings['rootFormat']; - - $this->objectsLoadMode = [ - PP_CHILDREN_FETCH_NONE => 'none', - PP_CHILDREN_FETCH_SELECTED => 'selected', - PP_CHILDREN_FETCH_ALL => 'all', - PP_CHILDREN_FETCH_PAGED => 'paged', - ]; - } - - public function userIndex() - { - // Bad path - if ($this->request->IsBadPath()) { - $this->response->notFound(); - return; - } - - // Get path - $urlPath = $this->request->getHostAndDir(); - $urlFile = $this->request->getFile(); - $urlPart = $this->request->getPart(); - - $this->tree->setFormat($this->rootFormatId); - $this->tree->setAliases($this->_parseAliasesConfig()); - - // Loading struct tree - $this->tree->load($urlPath); - $this->tree->getLinks(); - - // Loading content objects - $this->loadObjects($this->tree, $urlFile, $this->objects); - $this->objects->getLinks(); - - // Loading subcontent objects - $this->loadObjects($this->objects, $urlPart, $this->subObjects); - $this->subObjects->getLinks(); - - $this->check404error($urlFile, $urlPart); - } - - public function check404error($urlFile, $urlPart) - { - // Определение кода ошибки - if ( - !$this->tree->hasCurrent() || - !($this->request->isIndexFile($urlFile) || $this->objects->hasCurrent()) || - !($this->request->isIndexFile($urlPart) || $this->subObjects->hasCurrent()) - ) { - $this->response->notFound(); - } else { - $this->response->setOk(); - } - } - - protected - function _parseAliasesConfig() - { - if (!isset($this->settings['domainAlias'])) { - $this->settings['domainAlias'] = []; - return []; - } - - $config = $this->settings['domainAlias']; - if (is_string($config)) { - $config = explode("\n", $config); - } - - $aliases = []; - foreach ($config as $row) { - [$host, $alias] = preg_split('/\s*=\s*/', trim($row)); - $aliases[$host] = $alias; - } - - return $aliases; - } - - private function loadObjects($object, $pathName, $fillObj) - { - if (!$object->hasCurrent()) { - return; - } - - $allowed = $object->getAllowedChilds(); - - if (!count($allowed)) { - return; - } - - foreach ($allowed as $type => $behaviour) { - $format = \PXRegistry::getTypes($type); - - if (!is_object($format)) { - continue; - } - - $loadingMethod = 'load' . ucfirst($this->objectsLoadMode[$behaviour]) . 'Objects'; - $objsArray = $this->$loadingMethod($object, $format, $pathName); - - $fillObj->add($type, $objsArray); - $fillObj->findCurrent($type, $pathName); - } - } - - private function loadAllObjects($object, $format, $pathName) - { - $objsArray = []; - - $objsArray = $this->_loadContent($format, true, 'parent', $object->currentId); - - return $objsArray; - } - - private function loadNoneObjects($object, $format, $pathName) - { - return []; - } - - private function loadPagedObjects($object, $format, $pathName) - { - $objsArray = []; + if (!$this->request->isIndexFile($pathName)) { + $objsArray = $this->loadSelectedObjects($object, $format, $pathName); + + } else { + $a_per_page = $this->app->getProperty(strtoupper((string) $format->id) . '_PER_PAGE', DEFAULT_CHILDREN_PER_PAGE); + $count = $this->_loadContent($format, true, 'parent', $object->currentId, DB_SELECT_COUNT); + $currentPage = $this->getCurrentPage($format, $count, $a_per_page); + + $this->layout->assign('FP_' . strtoupper((string) $format->id) . '_TOTAL', $count); + $this->layout->assign('FP_' . strtoupper((string) $format->id) . '_PER_PAGE', $a_per_page); + $this->layout->assign('FP_' . strtoupper((string) $format->id) . '_DEFAULT_PAGE', $currentPage); + + $objsArray = $this->_loadContentLimited($format, true, 'parent', $object->currentId, $a_per_page, $a_per_page * ($currentPage - 1)); + } + + return $objsArray; + } + + private function loadSelectedObjects($object, $format, $pathName) + { + $objsArray = []; + + if (!isset($format->fields['pathname'])) { + return; + } + + $objsArray = $this->_loadContentLimited($format, true, ['pathname' => $pathName, 'parent' => $object->currentId], 'IGNORED', 1, 0); + return $objsArray; + } - if (!$this->request->isIndexFile($pathName)) { - $objsArray = $this->loadSelectedObjects($object, $format, $pathName); - - } else { - $a_per_page = $this->app->getProperty(strtoupper($format->id) . '_PER_PAGE', DEFAULT_CHILDREN_PER_PAGE); - $count = $this->_loadContent($format, true, 'parent', $object->currentId, DB_SELECT_COUNT); - $currentPage = $this->getCurrentPage($format, $count, $a_per_page); - - $this->layout->assign('FP_' . strtoupper($format->id) . '_TOTAL', $count); - $this->layout->assign('FP_' . strtoupper($format->id) . '_PER_PAGE', $a_per_page); - $this->layout->assign('FP_' . strtoupper($format->id) . '_DEFAULT_PAGE', $currentPage); - - $objsArray = $this->_loadContentLimited($format, true, 'parent', $object->currentId, $a_per_page, $a_per_page * ($currentPage - 1)); - } - - return $objsArray; - } - - private function loadSelectedObjects($object, $format, $pathName) - { - $objsArray = []; - - if (!isset($format->fields['pathname'])) { - return; - } - - $objsArray = $this->_loadContentLimited($format, true, ['pathname' => $pathName, 'parent' => $object->currentId], 'IGNORED', 1, 0); - return $objsArray; - } + private function getCurrentPage($format, $count, $a_per_page) + { + // default page - from properties.ini (first or last) + $defaultPage = $this->app->getProperty('FP_' . strtoupper((string) $format->id) . '_DEFAULT_LAST_PAGE') ? ceil($count / $a_per_page) : 1; - private function getCurrentPage($format, $count, $a_per_page) - { - // default page - from properties.ini (first or last) - $defaultPage = $this->app->getProperty('FP_' . strtoupper($format->id) . '_DEFAULT_LAST_PAGE') ? ceil($count / $a_per_page) : 1; + $currentPage = $this->request->getVar('page', $defaultPage); + $currentPage = max(1, ($currentPage > ceil($count / $a_per_page) ? ceil($count / $a_per_page) : $currentPage)); - $currentPage = $this->request->getVar('page', $defaultPage); - $currentPage = max(1, ($currentPage > ceil($count / $a_per_page) ? ceil($count / $a_per_page) : $currentPage)); + return $currentPage; + } - return $currentPage; - } + public function adminJson() + { + $format = $this->request->getVar('f'); + $cl = $this->request->getVar('cl'); + $id = $this->request->getVar('id'); + $answer = ''; + if (!empty($id) && isset($this->app->types[$format]) && ($this->app->types[$format]->struct == 'tree' && $this->request->GetVar($format . '_view') != 'plain')) { - public function adminJson() - { - $format = $this->request->getVar('f'); - $cl = $this->request->getVar('cl'); - $id = $this->request->getVar('id'); - $answer = ''; - if (!empty($id) && isset($this->app->types[$format]) && ($this->app->types[$format]->struct == 'tree' && $this->request->GetVar($format . '_view') != 'plain')) { + // {Нам это необходимо, чтобы работал PXAdminAjaxTreeObjects + $layout = new AdminHtmlLayout("index", $this->app->types); + $layout->setGetVarToSave('area', $this->area); + \PXRegistry::setLayout($layout); + // нам это необходимо, чтобы работал PXAdminAjaxTreeObjects} - // {Нам это необходимо, чтобы работал PXAdminAjaxTreeObjects - $layout = new AdminHtmlLayout("index", $this->app->types); - $layout->setGetVarToSave('area', $this->area); - \PXRegistry::setLayout($layout); - // нам это необходимо, чтобы работал PXAdminAjaxTreeObjects} + $hTree = new \PXAdminAjaxTreeObjects($format, null, null, true, $id); + isset($cl) && $hTree->showChildren($cl); - $hTree = new \PXAdminAjaxTreeObjects($format, null, null, true, $id); - isset($cl) && $hTree->showChildren($cl); + $answer = $hTree->html(); + } + return ['branch' => $answer]; + } - $answer = $hTree->html(); - } - return ['branch' => $answer]; - } + public function adminIndex() + { + if (!isset($this->app->types[$this->rootFormatId])) { + FatalError("Некорректный тип данных"); + } - public function adminIndex() - { - if (!isset($this->app->types[$this->rootFormatId])) { - FatalError("Некорректный тип данных"); - } + $app = $this->app; + $request = $this->request; + $layout = $this->layout; - $app = $this->app; - $request = $this->request; - $layout = $this->layout; + $this->rootFormat = $app->types[$this->rootFormatId]; + + $rqSid = $request->getSid(); + $rqCid = $request->getCid(); + + $hTree = new \PXAdminAjaxTreeObjects($this->rootFormat, null, null, true); - $this->rootFormat = $app->types[$this->rootFormatId]; - - $rqSid = $request->getSid(); - $rqCid = $request->getCid(); - - $hTree = new \PXAdminAjaxTreeObjects($this->rootFormat, null, null, true); + if ($this->rootFormatId == $this->app->modules['main']->getSetting('rootFormat')) { + $this->fillPathnamedAliases($hTree->getTree()); + } - if ($this->rootFormatId == $this->app->modules['main']->getSetting('rootFormat')) { - $this->fillPathnamedAliases($hTree->getTree()); - } + $hTree->hideCaption(); + $hTree->showChildren('sid'); - $hTree->hideCaption(); - $hTree->showChildren('sid'); + if ($rqSid != null && $hTree->has($rqSid)) { + $hTree->setControlParent($rqSid); + $hTree->setSelected($rqSid); - if ($rqSid != null && $hTree->has($rqSid)) { - $hTree->setControlParent($rqSid); - $hTree->setSelected($rqSid); - - $parentObject = $hTree->get($rqSid); - - $layout->assignTitle('раздел «' . $parentObject['title'] . '»'); - - $cidType = null; - $cidObject = null; - - $structHasInnerBlocks = false; - - foreach ($this->rootFormat->fields as $fieldName => $field) { - if (!is_a($field->storageType, 'PXStorageTypeBlockcontent')) { - continue; - } - - $blocks = new \PXAdminBlocksTree($this->rootFormat, $fieldName, $rqSid); - $blocks->addToParent('INNER.1.0'); - } - - $allowedChilds = $app->getAllowedChildsKeys($this->rootFormat->id, $parentObject); - if (count($allowedChilds)) { - foreach ($allowedChilds as $childFormat) { - if (!isset($app->types[$childFormat])) { - $layout->append('INNER.1.0', '

Формат ' . $childFormat . ' не описан

'); - continue; - } - - if ($app->types[$childFormat]->struct == 'tree' && $request->GetVar($childFormat . '_view') != 'plain') { - $objects = new \PXAdminTreeObjects($childFormat, $rqSid, 'ByParent', true); - - } else { - $objects = new \PXAdminTableObjects($childFormat, $rqSid, 'ByParent'); - } - - $objects->showChildren('cid'); - $objects->setControlParent($rqSid); - $objects->addToParent('INNER.1.0'); - - if ($rqCid && $childFormat == $request->getCtype()) { - $layout->setGetVarToSave('ctype', $request->getCtype()); - - if ($objects->has($rqCid)) { - $cidType = $childFormat; - $cidObject = $objects->get($rqCid); - } - } - } - - if ($rqCid && !empty($cidType)) { - $layout->setGetVarToSave('cid', $rqCid); - - $childHasInnerBlocks = false; - foreach ($this->app->types[$cidType]->fields as $fieldName => $field) { - if (!is_a($field->storageType, 'PXStorageTypeBlockcontent')) { - continue; - } - $blocks = new \PXAdminBlocksTree($this->app->types[$cidType], $fieldName, $rqCid); - $blocks->addToParent('INNER.2.0'); - $childHasInnerBlocks = true; - } - - $allowedChilds = $app->getAllowedChildsKeys($cidType, $cidObject); - $layout->setThreeColumns(); - - if (count($allowedChilds)) { - foreach ($allowedChilds as $childFormat) { - if ($app->types[$childFormat]->struct == 'tree' && $request->GetVar($childFormat . '_view') != 'plain') { - $subObjects = new \PXAdminTreeObjects($childFormat, $rqCid, 'ByParent'); - - } else { - $subObjects = new \PXAdminTableObjects($childFormat, $rqCid, 'ByParent'); - } - - $subObjects->setControlParent($rqCid); - $subObjects->addToParent('INNER.2.0'); - } - - } elseif (!$childHasInnerBlocks) { - $this->layout->notSetAllowedChilds('INNER.2.0', $cidType, $rqCid); - } - } - - } elseif (!$structHasInnerBlocks) { - $this->layout->notSetAllowedChilds('INNER.1.0', $this->rootFormatId, $rqSid); - } - - } elseif ($rqSid != null) { - $layout->assign('INNER.1.0', '

Раздел ' . htmlspecialchars($rqSid, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) . ' не найден

'); - - } else { - $layout->assign('INNER.1.0', '

Выберите раздел

'); - } - - $hTree->addToParent('INNER.0.0'); - } - - public function _loadContent(&$format, $status, $param, $value, $flag = DB_SELECT_TABLE, $order = NULL) - { - return $this->db->GetObjectsByField($format, $status, $param, $value, $flag, $order); - } - - public function _loadContentLimited(&$format, $status, $param, $value, $limit, $offset, $flag = DB_SELECT_TABLE, $order = NULL) - { - return $this->db->GetObjectsByFieldLimited($format, $status, $param, $value, $limit, $offset, $flag, $order); - } - - // temporary struct root fixer. probably we must move it out to struct.class.inc (PXStructTree) - protected - function fillPathnamedAliases($tree, $host = null) - { - // "пустой" проект - if (empty($tree) or empty($tree->levels[1])) { - return; - } - - // set current host - is_null($host) && $host = \PXRegistry::getRequest()->getHTTPHost(); - $aliases = $this->_parseAliasesConfig(); - - // находим соответствие между хостом, на который мы зашли - fixme: use case? - // и корнем в дереве, который может быть нам нужен - $hostAlias = 'default'; - if (isset($aliases[$host])) { - $hostAlias = $aliases[$host]; - } - - // если алиас "не нашли", может быть нужный хост есть среди корней? - fixme: use case? - if ($hostAlias == 'default') { - foreach ($tree->levels[1] as $_rootId) { - if ($tree->leafs[$_rootId]->content['pathname'] === $host) { - $hostAlias = $host; - break; - } - } - } - - // находим нужный корень и правим его pathname по полному соответствию маске - foreach ($tree->levels[1] as $_rootId) { - if ($tree->leafs[$_rootId]->content['pathname'] === $hostAlias) { - $tree->leafs[$_rootId]->content['__pathname'] = $tree->leafs[$_rootId]->content['pathname']; - $tree->leafs[$_rootId]->content['pathname'] = $host; - break; - } - } - } + $parentObject = $hTree->get($rqSid); + + $layout->assignTitle('раздел «' . $parentObject['title'] . '»'); + + $cidType = null; + $cidObject = null; + + $structHasInnerBlocks = false; + + foreach ($this->rootFormat->fields as $fieldName => $field) { + if (!is_a($field->storageType, 'PXStorageTypeBlockcontent')) { + continue; + } + + $blocks = new \PXAdminBlocksTree($this->rootFormat, $fieldName, $rqSid); + $blocks->addToParent('INNER.1.0'); + } + + $allowedChilds = $app->getAllowedChildsKeys($this->rootFormat->id, $parentObject); + if (count($allowedChilds)) { + foreach ($allowedChilds as $childFormat) { + if (!isset($app->types[$childFormat])) { + $layout->append('INNER.1.0', '

Формат ' . $childFormat . ' не описан

'); + continue; + } + + if ($app->types[$childFormat]->struct == 'tree' && $request->GetVar($childFormat . '_view') != 'plain') { + $objects = new \PXAdminTreeObjects($childFormat, $rqSid, 'ByParent', true); + + } else { + $objects = new \PXAdminTableObjects($childFormat, $rqSid, 'ByParent'); + } + + $objects->showChildren('cid'); + $objects->setControlParent($rqSid); + $objects->addToParent('INNER.1.0'); + + if ($rqCid && $childFormat == $request->getCtype()) { + $layout->setGetVarToSave('ctype', $request->getCtype()); + + if ($objects->has($rqCid)) { + $cidType = $childFormat; + $cidObject = $objects->get($rqCid); + } + } + } + + if ($rqCid && !empty($cidType)) { + $layout->setGetVarToSave('cid', $rqCid); + + $childHasInnerBlocks = false; + foreach ($this->app->types[$cidType]->fields as $fieldName => $field) { + if (!is_a($field->storageType, 'PXStorageTypeBlockcontent')) { + continue; + } + $blocks = new \PXAdminBlocksTree($this->app->types[$cidType], $fieldName, $rqCid); + $blocks->addToParent('INNER.2.0'); + $childHasInnerBlocks = true; + } + + $allowedChilds = $app->getAllowedChildsKeys($cidType, $cidObject); + $layout->setThreeColumns(); + + if (count($allowedChilds)) { + foreach ($allowedChilds as $childFormat) { + if ($app->types[$childFormat]->struct == 'tree' && $request->GetVar($childFormat . '_view') != 'plain') { + $subObjects = new \PXAdminTreeObjects($childFormat, $rqCid, 'ByParent'); + + } else { + $subObjects = new \PXAdminTableObjects($childFormat, $rqCid, 'ByParent'); + } + + $subObjects->setControlParent($rqCid); + $subObjects->addToParent('INNER.2.0'); + } + + } elseif (!$childHasInnerBlocks) { + $this->layout->notSetAllowedChilds('INNER.2.0', $cidType, $rqCid); + } + } + + } elseif (!$structHasInnerBlocks) { + $this->layout->notSetAllowedChilds('INNER.1.0', $this->rootFormatId, $rqSid); + } + + } elseif ($rqSid != null) { + $layout->assign('INNER.1.0', '

Раздел ' . htmlspecialchars((string) $rqSid, ENT_COMPAT | ENT_HTML401, DEFAULT_CHARSET) . ' не найден

'); + + } else { + $layout->assign('INNER.1.0', '

Выберите раздел

'); + } + + $hTree->addToParent('INNER.0.0'); + } + + public function _loadContent(&$format, $status, $param, $value, $flag = DB_SELECT_TABLE, $order = null) + { + return $this->db->GetObjectsByField($format, $status, $param, $value, $flag, $order); + } + + public function _loadContentLimited(&$format, $status, $param, $value, $limit, $offset, $flag = DB_SELECT_TABLE, $order = null) + { + return $this->db->GetObjectsByFieldLimited($format, $status, $param, $value, $limit, $offset, $flag, $order); + } + + // temporary struct root fixer. probably we must move it out to struct.class.inc (PXStructTree) + protected function fillPathnamedAliases($tree, $host = null) + { + // "пустой" проект + if (empty($tree) or empty($tree->levels[1])) { + return; + } + + // set current host + is_null($host) && $host = \PXRegistry::getRequest()->getHTTPHost(); + $aliases = $this->_parseAliasesConfig(); + + // находим соответствие между хостом, на который мы зашли - fixme: use case? + // и корнем в дереве, который может быть нам нужен + $hostAlias = 'default'; + if (isset($aliases[$host])) { + $hostAlias = $aliases[$host]; + } + + // если алиас "не нашли", может быть нужный хост есть среди корней? - fixme: use case? + if ($hostAlias == 'default') { + foreach ($tree->levels[1] as $_rootId) { + if ($tree->leafs[$_rootId]->content['pathname'] === $host) { + $hostAlias = $host; + break; + } + } + } + + // находим нужный корень и правим его pathname по полному соответствию маске + foreach ($tree->levels[1] as $_rootId) { + if ($tree->leafs[$_rootId]->content['pathname'] === $hostAlias) { + $tree->leafs[$_rootId]->content['__pathname'] = $tree->leafs[$_rootId]->content['pathname']; + $tree->leafs[$_rootId]->content['pathname'] = $host; + break; + } + } + } } diff --git a/src/Module/MassChangeModule.php b/src/Module/MassChangeModule.php index 1bf4e45b..2f7ee3d1 100644 --- a/src/Module/MassChangeModule.php +++ b/src/Module/MassChangeModule.php @@ -7,139 +7,144 @@ * * @package PP\Module */ -class MassChangeModule extends AbstractModule { - - protected $helper; - - public function adminJson() { - $this->helper = new \stdClass(); - $this->helper->objectType = $this->request->getVar('format'); - $this->helper->operation = preg_replace("#[^a-z0-9_-]#i", '', $this->request->getVar('handler')); - $this->helper->options = $this->request->getVar('options'); - - if (!empty($this->helper->options)) { - $this->helper->options = json_decode($this->helper->options); - } - - $this->helper->options = is_object($this->helper->options) ? $this->helper->options : null; - $this->helper->objectIds = (array)$this->request->getVar('objects', []); - $this->helper->objectIds = array_filter($this->helper->objectIds, 'is_numeric'); - - if (!(isset($this->app->types[$this->helper->objectType]) || count($this->helper->objectIds))) { - FatalError('Malformed action params'); - } - - return $this->doOperation(); - } - - /** - * @return null|object - */ - protected function doChangeParent() { - $dtype = $this->app->types[$this->helper->objectType]; - $parentField = isset($dtype->fields['parent']) - ? 'parent' - : (isset($dtype->fields['pid']) ? 'pid' : null); - - $newParent = !empty($this->helper->options->parent) && is_numeric($this->helper->options->parent) - ? $this->helper->options->parent - : 0; - - if ($newParent && $parentField) { - foreach ($this->helper->objectIds as $id) { - $object = $this->db->GetObjectById($dtype, $id); - if (!$object || $object[$parentField] == $newParent) { - continue; - } - - $object[$parentField] = $newParent; - $this->db->ModifyContentObject($dtype, $object); - } - - return null; - } - - return \PXEngineJSON::toError('Недопустимый родитель объекта'); - } - - /** - * @return object - */ - protected function doCommonMultipleDelete() { - foreach ($this->helper->objectIds as $objectId) { - $this->db->DeleteContentObject($this->app->types[$this->helper->objectType], $objectId); - } - - $cnt = count($this->helper->objectIds); - $res = sprintf( - '%s %d %s', - NumericEndingsRussian($cnt, 'удалён', 'удалено', 'удалено'), - $cnt, - NumericEndingsRussian($cnt, 'объект', 'объекта', 'объектов') - ); - - return \PXEngineJSON::toSuccess($res); - } - - /** - * @return null|object - */ - protected function doCommonMultipleStatusChange() { - $dtype = $this->app->types[$this->helper->objectType]; - $states = ['true' => true, 'false' => false]; - $status = (isset($this->helper->options->status) && in_array($this->helper->options->status, ['true', 'false'])) - ? $this->helper->options->status - : null; - - if (isset($states[$status])) { - $status = $states[$status]; - foreach ($this->helper->objectIds as $id) { - $object = $this->db->GetObjectById($dtype, $id); - if (!$object || $object['status'] == $status) { - continue; - } - - $object['status'] = $status; - $this->db->ModifyContentObject($dtype, $object); - } - - return null; - } - - return \PXEngineJSON::toError('Недопустимый статус объекта'); - } - - /** - * @return object - */ - protected function doOperation() { - $operationName = strtolower($this->helper->operation); - $result = null; - - // operation names defined in lib/HTML/Admin/Widgets/Multiops/*.class.inc - switch ($operationName) { - case 'docommonmultiplestatuschange': - $result = $this->doCommonMultipleStatusChange(); - break; - - case 'dochangeparent': - $result = $this->doChangeParent(); - break; - - case 'docommonmultipledelete': - $result = $this->doCommonMultipleDelete(); - break; - } - - $result = ($result === null) - ? (object)$result - : $result; - - if (isset($result->redirect)) { - $this->response->redirect($result->redirect); - } - - return $result; - } +class MassChangeModule extends AbstractModule +{ + protected $helper; + + public function adminJson() + { + $this->helper = new \stdClass(); + $this->helper->objectType = $this->request->getVar('format'); + $this->helper->operation = preg_replace("#[^a-z0-9_-]#i", '', $this->request->getVar('handler')); + $this->helper->options = $this->request->getVar('options'); + + if (!empty($this->helper->options)) { + $this->helper->options = json_decode($this->helper->options); + } + + $this->helper->options = is_object($this->helper->options) ? $this->helper->options : null; + $this->helper->objectIds = (array)$this->request->getVar('objects', []); + $this->helper->objectIds = array_filter($this->helper->objectIds, 'is_numeric'); + + if (!(isset($this->app->types[$this->helper->objectType]) || count($this->helper->objectIds))) { + FatalError('Malformed action params'); + } + + return $this->doOperation(); + } + + /** + * @return null|object + */ + protected function doChangeParent() + { + $dtype = $this->app->types[$this->helper->objectType]; + $parentField = isset($dtype->fields['parent']) + ? 'parent' + : (isset($dtype->fields['pid']) ? 'pid' : null); + + $newParent = !empty($this->helper->options->parent) && is_numeric($this->helper->options->parent) + ? $this->helper->options->parent + : 0; + + if ($newParent && $parentField) { + foreach ($this->helper->objectIds as $id) { + $object = $this->db->GetObjectById($dtype, $id); + if (!$object || $object[$parentField] == $newParent) { + continue; + } + + $object[$parentField] = $newParent; + $this->db->ModifyContentObject($dtype, $object); + } + + return null; + } + + return \PXEngineJSON::toError('Недопустимый родитель объекта'); + } + + /** + * @return object + */ + protected function doCommonMultipleDelete() + { + foreach ($this->helper->objectIds as $objectId) { + $this->db->DeleteContentObject($this->app->types[$this->helper->objectType], $objectId); + } + + $cnt = count($this->helper->objectIds); + $res = sprintf( + '%s %d %s', + NumericEndingsRussian($cnt, 'удалён', 'удалено', 'удалено'), + $cnt, + NumericEndingsRussian($cnt, 'объект', 'объекта', 'объектов') + ); + + return \PXEngineJSON::toSuccess($res); + } + + /** + * @return null|object + */ + protected function doCommonMultipleStatusChange() + { + $dtype = $this->app->types[$this->helper->objectType]; + $states = ['true' => true, 'false' => false]; + $status = (isset($this->helper->options->status) && in_array($this->helper->options->status, ['true', 'false'])) + ? $this->helper->options->status + : null; + + if (isset($states[$status])) { + $status = $states[$status]; + foreach ($this->helper->objectIds as $id) { + $object = $this->db->GetObjectById($dtype, $id); + if (!$object || $object['status'] == $status) { + continue; + } + + $object['status'] = $status; + $this->db->ModifyContentObject($dtype, $object); + } + + return null; + } + + return \PXEngineJSON::toError('Недопустимый статус объекта'); + } + + /** + * @return object + */ + protected function doOperation() + { + $operationName = strtolower($this->helper->operation); + $result = null; + + // operation names defined in lib/HTML/Admin/Widgets/Multiops/*.class.inc + switch ($operationName) { + case 'docommonmultiplestatuschange': + $result = $this->doCommonMultipleStatusChange(); + break; + + case 'dochangeparent': + $result = $this->doChangeParent(); + break; + + case 'docommonmultipledelete': + $result = $this->doCommonMultipleDelete(); + break; + } + + $result = ($result === null) + ? (object)$result + : $result; + + if (isset($result->redirect)) { + $this->response->redirect($result->redirect); + } + + return $result; + } } diff --git a/src/Module/ModuleInterface.php b/src/Module/ModuleInterface.php index 023cf190..d8e044c3 100644 --- a/src/Module/ModuleInterface.php +++ b/src/Module/ModuleInterface.php @@ -9,45 +9,45 @@ * * @package PP\Module */ -interface ModuleInterface extends ContainerAwareInterface { - - public const ACTION_INDEX = 'index'; - public const ACTION_ACTION = 'action'; - public const ACTION_JSON = 'json'; - public const ACTION_POPUP = 'popup'; - - /** - * @return mixed - */ - public function adminIndex(); - - /** - * @return string - */ - public function adminAction(); - - /** - * @return string - */ - public function adminPopup(); - - /** - * @return mixed - */ - public function userIndex(); - - /** - * @return mixed - */ - public function userAction(); - - /** - * @return array|null - */ - public function userJson(); - - /** - * @return string - */ - public function adminJson(); +interface ModuleInterface extends ContainerAwareInterface +{ + public const ACTION_INDEX = 'index'; + public const ACTION_ACTION = 'action'; + public const ACTION_JSON = 'json'; + public const ACTION_POPUP = 'popup'; + + /** + * @return mixed + */ + public function adminIndex(); + + /** + * @return string + */ + public function adminAction(); + + /** + * @return string + */ + public function adminPopup(); + + /** + * @return mixed + */ + public function userIndex(); + + /** + * @return mixed + */ + public function userAction(); + + /** + * @return array|null + */ + public function userJson(); + + /** + * @return string + */ + public function adminJson(); } diff --git a/src/Module/ObjectsModule.php b/src/Module/ObjectsModule.php index 383a11d2..3254a65a 100644 --- a/src/Module/ObjectsModule.php +++ b/src/Module/ObjectsModule.php @@ -9,565 +9,569 @@ */ class ObjectsModule extends AbstractModule { - public $formats; - public $object; - - public function __construct($area, $settings) - { - parent::__construct($area, $settings); - - $this->formats = []; - - if (isset($settings['format'])) { - if (is_array($settings['format'])) { - $this->formats = $settings['format']; - } else { - $this->formats[] = $settings['format']; - } - } - } - - public function adminIndex() - { - $rSid = $this->request->getSid(); - - if (!$this->indexSetMenu($rSid)) { - return; - } - - $this->datatype = $this->getDatatype('sid'); - $format = $this->datatype->id; - - $rView = $this->request->getVar($format . '_view'); + public $formats; + public $object; + + public function __construct($area, $settings) + { + parent::__construct($area, $settings); + + $this->formats = []; + + if (isset($settings['format'])) { + if (is_array($settings['format'])) { + $this->formats = $settings['format']; + } else { + $this->formats[] = $settings['format']; + } + } + } + + public function adminIndex() + { + $rSid = $this->request->getSid(); + + if (!$this->indexSetMenu($rSid)) { + return; + } + + $this->datatype = $this->getDatatype('sid'); + $format = $this->datatype->id; + + $rView = $this->request->getVar($format . '_view'); - if ($rView === 'plain' || $this->datatype->struct === 'plain') { - $this->indexAppendTable(); - } else { - $this->indexAppendTree(); - } - } - - public function indexSetMenu($rSid) - { - $types = []; - $canProceed = false; - foreach ($this->app->types as $k => $v) { - if ((count($this->formats) == 0 || in_array($k, $this->formats)) && $this->user->can('admin', $v)) { - $types[$k] = $v->title; - $canProceed = $canProceed || $rSid == $k; - } - } - - uasort($types, [$this, '__sortTypes']); - - $this->layout->assignKeyValueList('INNER.0.0', $types, $rSid); - return $canProceed; - } - - public function indexAppendTable() - { - $table = new \PXAdminTableObjects($this->datatype); - $table->addToParent('INNER.1.0'); - - $cId = $this->request->getCid(); - $table->showChildren('cid'); - - if ($cId && $table->has($cId)) { - $this->indexAppendChild($table->get($cId)); - } - } - - public function indexAppendChild($cidObject) - { - $format = $this->datatype->id; - $rqCid = $this->request->getCid(); - - $this->layout->setThreeColumns(); - $this->layout->setGetVarToSave('cid', $rqCid); - - $allowedChilds = $this->app->GetAllowedChildsKeys($format, $cidObject); - - if (count($allowedChilds)) { - foreach ($allowedChilds as $childFormat) { - if ($this->app->types[$childFormat]->struct == 'tree' && $this->request->GetVar($childFormat . '_view') != 'plain') { - $objects = new \PXAdminTreeObjects($childFormat, $rqCid, 'ByParent'); - - } else { - $objects = new \PXAdminTableObjects($childFormat, $rqCid, 'ByParent'); - } - - $objects->setControlParent($rqCid); - $objects->addToParent('INNER.2.0'); - } - - } else { - $this->layout->notSetAllowedChilds('INNER.2.0', $format, $rqCid); - } - } - - - public function indexAppendTree() - { - $objects = new \PXAdminAjaxTreeObjects($this->datatype); - $objects->addToParent('INNER.1.0'); - } - - public function adminPopup() - { - $this->datatype = $this->getDatatype(); - $object = $this->popupGetObject(); - - if (!$this->user->isAdmin() || empty($object)) { - return $this->layout->assignError('OUTER.CONTENT', 'Нет доступа'); - } - - $action = $this->request->getAction(); - - $this->layout->setGetVarToSave('id', $this->request->getId()); - $this->layout->setGetVarToSave('format', $this->datatype->id); - $this->layout->setGetVarToSave('action', $action); - - $counts = []; - $possibleFormats = $this->datatype->childTypes(); - - foreach ($this->datatype->allowedChildTypes($object) as $allowedFormat => $childType) { - $counts[$allowedFormat] = $this->db->getObjectsByParent($childType, NULL, $object['id'], DB_SELECT_COUNT); - } - - $this->db->LoadDirectoriesByType($this->datatype, $object); - - // warn about parent if need - if (!empty($this->datatype->parent) && empty($object['parent']) && $this->datatype->struct != 'tree') { - $parentDatatype = $this->app->types[$this->datatype->parent]; - - \PXDecorativeWidgetsCollection::addToCollection( - "Внимание! " . - "Объект типа '{$this->datatype->title}' может не быть создан вне контекста объекта '{$parentDatatype->title}'.
" . - "Рекомендуется не использовать данный функционал для создания объектов данного типа.", - 'PXAdminForm', - 'notices', - \PXAdminForm::NOTICES_CONTENT - ); - } - - $form = new \PXAdminForm($object, $this->datatype); - $form->setDisabledStatus($this->popupCheckAccess($object)); - $form->setAction($this->request->getAction()); - $form->setArea($this->getArea()); - $form->setChildren($possibleFormats, $counts); - $form->setLinks($this->popupLinks($object)); - $form->setTitle($this->popupSetTitle($object)); - $form->getForm(); - $form->setDisabledStatus(false); //release global NLAbstractHTMLForm lock! - } - - public function &getDatatype($varName = 'format') - { - $rFormat = $this->request->getVar($varName); - if (!isset($this->app->types[$rFormat])) { - FatalError('Undefined datatype ' . $rFormat); - } - return $this->app->types[$rFormat]; - } - - public function popupGetObject() - { - $rId = $this->request->getId(); - $object = []; - - if ($rId == 0) { - $object = $this->app->initContentObject($this->datatype->id); - $rObject = $this->request->getContentObject($this->datatype); - - foreach ($rObject as $k => $v) { - if (array_key_exists($k, $object) && is_null($object[$k])) { - $object[$k] = $v; - } - } - } else { - if (!sizeof($object = $this->db->getObjectById($this->datatype, $rId))) { - FatalError('Объект не существует !'); - } - - if (!$this->user->can('admin', $this->datatype, $object)) { - $object = []; - } - } - - if (isset($object['sys_owner'])) { - $tmp = $this->db->getObjectById($this->app->types['suser'], $object['sys_owner']); - $object['ownerlogin'] = isset($tmp['title']) ? $tmp['title'] : ''; - } - - return $object; - } - - public function popupCheckAccess($object) - { - if (!$this->user->can(['write', (($this->request->getId() > 0) ? 'modify' : 'add')], $this->datatype, $object)) { - return true; - } - } - - public function popupSetTitle($object) - { - $title = ''; - if ($this->request->getId() > 0) { - if (isset($object) && isset($object['title'])) { - $title = '«' . mb_substr(strip_tags($object['title']), 0, 32) . '» — '; - } - $title .= ' Редактирование'; - } else { - $title = 'Добавление'; - } - $title .= ' объекта формата «' . strip_tags($this->datatype->title) . '»'; - return $title; - } - - public function popupLinks($object) - { - $db =& $this->db; - $app =& $this->app; - $datatype =& $this->datatype; - $request =& $this->request; - $layout =& $this->layout; - - $linksParam = []; - - $refFilters = (array)$request->GetVar('filters', []); - foreach ($datatype->references as $k => $reference) { - if ($reference->hidden) { - continue; - } - $refDatatype = $app->types[$k]; - - $existingLinks = $db->getLinks($reference, $datatype->id, $object['id']); - - $onPage = $app->getProperty('LINKS_ON_PAGE', 10); - $currentPage = $request->getVar($k . '_page', 1); - $layout->setGetVarToSave($k . '_page', $currentPage); - - $onlyExistingLinks = $request->GetVar($k . '_exist', $reference->byDefault); - $layout->setGetVarToSave($k . '_exist', $onlyExistingLinks); - - $filteredWhere = $this->applyFiltersToWhere($refFilters, $refDatatype); - $trueCond = $db->TrueStatusString() . '=' . $db->TrueStatusString(); - $falseCond = $db->TrueStatusString() . '=' . $db->TrueStatusString(false); - - if ($onlyExistingLinks) { - if (count($existingLinks)) { - $existingLinksWhere = $k . '.id IN ( ' . join(',', array_keys($existingLinks)) . ' ) '; - } else { - $existingLinksWhere = $falseCond; - } - [$possibleLinks, $pagerCount, $overallCount] = $this->_getLinksData($refDatatype, $existingLinksWhere . $filteredWhere, $trueCond, $onPage, $currentPage); - } else { - $mainWhere = $trueCond; //No stupid NULL - - if (!empty($reference->filterFrom) && $reference->from == $k) { - $mainWhere = '(' . $db->parseWhereTemplate($reference->filterFrom, $object, $datatype) . ')'; - } - - if (!empty($reference->filterTo) && $reference->to == $k) { - $mainWhere = '(' . $db->parseWhereTemplate($reference->filterTo, $object, $datatype) . ')'; - } - - if (!empty($reference->restrictBy)) { - $mainWhere .= ' AND '; - $mainWhere .= $k . '.' . $reference->restrictBy . " = '" . $db->escapeString($object[$reference->restrictBy]) . "'"; - $mainWhere = '(' . $mainWhere . ')'; - } - [$possibleLinks, $pagerCount, $overallCount] = $this->_getLinksData($refDatatype, $mainWhere . $filteredWhere, $mainWhere, $onPage, $currentPage); - if ($datatype->id == $k) { - unset($possibleLinks[$object['id']]); - } - } - - $linksParam[$k] = [ - "reference" => $reference, - "formatTo" => $refDatatype, - "links" => $existingLinks, - "pLinks" => $possibleLinks, - "objectsOnPage" => $onPage, - "page" => $currentPage, - "count" => $pagerCount, - "fullCount" => $overallCount, - "onlyExistingLinks" => $onlyExistingLinks, - "filters" => $refFilters, - ]; - } - return $linksParam; - } - - public function _getLinksData($dType, $where, $whereAllCount, $onPage, $cPage) - { - $pLinks = $this->db->getObjectsByWhereLimited($dType, NULL, $where, $onPage, $onPage * ($cPage - 1)); - $pCount = $this->db->getObjectsByWhere($dType, NULL, $where, DB_SELECT_COUNT); - $oCount = $this->db->getObjectsByWhere($dType, NULL, $whereAllCount, DB_SELECT_COUNT); - - return [$pLinks, $pCount, $oCount]; - } - - public function adminAction() - { - $this->datatype =& $this->getDatatype(); - - $action = $this->_route(); - switch ($action) { - default: //FIXME: default main action it is right behaviour ? - case 'main': - $this->actionMain(); - break; - - case 'children': - $this->actionChildren(); - break; - - case 'remove': - $this->actionRemove(); - break; - - case 'directup': - $this->actionUpContentObject(); - break; - - case 'directdown': - $this->actionDownContentObject(); - break; - - case 'directmove': - $this->actionMoveContentObject(); - break; - - case 'directremove': - $this->actionRemoveDirect(); - break; - - case 'directstatus': - $this->actionChangeStatus(); - break; - - case 'clone': - $this->actionCloneContentObject(); - break; - - case 'links': - $this->actionModifyLinks(); - break; - - case 'auditlog': - //do nothing. FIXME: make actions configurable (like in multioperations module), not statically written here ! - break; - } - return $this->nextLocation; - } - - protected function getArea() - { - return 'objects'; - } - - public function _route() - { - $rAction = $this->request->getAction(); - $this->nextLocation = 'popup.phtml?area=' . $this->getArea() . '&format=' . $this->datatype->id . '&action=' . $rAction; - return $rAction; - } - - public function actionMain() - { - $object = $this->request->getContentObject($this->datatype); - - if ($object['id']) { - $this->db->modifyContentObject($this->datatype, $object, true); // Preserve values with displaytype HIDDEN or STATIC - $this->returnToId($object['id']); - - } else { - $id = $this->db->addContentObject($this->datatype, $object); - $this->returnToId($id); - } - } - - public function actionCloneContentObject() - { - $donor = $this->request->getContentObject($this->datatype); - - if (!is_numeric($donor['id']) || !$donor['id']) { - FatalError('Клонировать можно только существующие объекты'); - } - - $cloneId = $this->db->cloneContentObject($this->datatype, $donor['id']); - $this->returnToId($cloneId, 'main'); - } - - public function actionChildren() - { - if (!count($this->datatype->childs)) { - FatalError("В этом разделе невозможно назначать потомков"); - } - - $object = $this->request->getObjectSysVars($this->datatype, [OBJ_FIELD_CHILDREN]); - - if ($object['id']) { - $this->db->modifyObjectSysVars($this->datatype, $object); - $this->returnToId($object['id']); - - } else { - $this->returnToReferer(); - } - } - - public function actionRemove() - { - if ($this->request->getAck()) { - $this->db->deleteContentObject($this->datatype, $this->request->getID()); - } - - closeAndRefresh(); - } - - public function actionRemoveDirect() - { - $this->db->deleteContentObject($this->datatype, $this->request->getId()); - $this->returnToReferer(); - } - - public function applyFiltersToUri($filters) - { - foreach ($filters as $dt => $fields) { - if (is_array($fields) && count($fields) > 0) { - foreach ($fields as $field => $filter) { - if (empty($filter)) continue; - $this->nextLocation .= '&filters[' . $dt . ']' . '[' . $field . ']=' . rawurlencode($filter); - } - } - } - } - - public function applyFiltersToWhere($refFilters, $refDatatype) - { - $filterOnWhere = NULL; - if (isset($refFilters[$refDatatype->id]) && - is_array($refFilters[$refDatatype->id]) && - count($refFilters[$refDatatype->id]) > 0 - ) { - $applied = 0; - $filterOnWhere = ' AND ('; - foreach ($refFilters[$refDatatype->id] as $field => $filter) { - if (empty($filter)) continue; - $applied++; - $this->layout->setGetVarToSave('filters[' . $refDatatype->id . '][' . $field . ']', $filter); - //apply simple filter logic, ex. : ^search string$ - $modifiers = P_LEFT | P_RIGHT; - if (substr($filter, 0, 1) == '^') { - $modifiers &= P_RIGHT; - $filter = substr($filter, 1); - } - if (substr($filter, -1) == '$') { - $modifiers &= P_LEFT; - $filter = substr($filter, 0, -1); - } - - // build where - $filterOnWhere .= $refDatatype->id . '.' . $this->db->escapeString($field) . $this->db->LIKE($filter, $modifiers) . " AND "; - } - - if (!$applied) { - $filterOnWhere = NULL; - } else { - $filterOnWhere = substr($filterOnWhere, 0, -4); //remove last AND or OR - $filterOnWhere .= ')'; - } - } - return $filterOnWhere; - } - - public function appendLinksExistFlag() - { - foreach ($this->request->GetAllPostData() as $k => $v) { - if (is_string($k) && strstr($k, '_exist') == '_exist') { - $this->nextLocation .= "&{$k}={$v}"; - } - } - } - - public function returnToId($id, $action = null) - { - $this->nextLocation .= '&id=' . $id; - - if (is_string($action)) { - $this->nextLocation .= '&action=' . $action; - } - } - - public function returnToReferer() - { - $this->nextLocation = $this->request->GetReferer(); - } - - public function actionUpContentObject() - { - $this->db->upContentObject($this->datatype, $this->request->getId()); - $this->returnToReferer(); - } - - public function actionDownContentObject() - { - $this->db->downContentObject($this->datatype, $this->request->getId()); - $this->returnToReferer(); - } - - public function actionMoveContentObject() - { - $this->db->moveContentObject($this->datatype, $this->request->getId(), $this->request->getVar('shift')); - $this->returnToReferer(); - } - - public function actionChangeStatus() - { - $object = $this->db->getObjectById($this->datatype, $this->request->getId()); - $object['status'] = !$object['status']; - $this->db->modifyContentObject($this->datatype, $object); - - $this->returnToReferer(); - } - - public function actionModifyLinks() - { - $id = $this->request->getId(); - foreach ($this->datatype->references as $reference) { - if ($reference->hidden) { - continue; - } - $ll = $this->_makeLinkedListFrom($reference); - $this->db->ModifyLinks($reference, $this->datatype->id, $id, $ll, false); - } - - if (count($filters = (array)$this->request->GetVar('filters', []))) { - $this->applyFiltersToUri($filters); - } - $this->appendLinksExistFlag(); - $this->returnToId($id); - } - - public function _makeLinkedListFrom($ref) - { - $linkedList = []; - foreach ($this->request->getLinks($ref) as $values) { - foreach ($values as $id => $data) { - $node = &$linkedList[$id]; - if (isset($linkedList[$id])) { - while (!is_null($node = &$node['next'])) ; - } - $node = $data; - $node['next'] = NULL; - } - } - return $linkedList; - } - - public function __sortTypes($a, $b) - { - return strcoll($a, $b); - } + if ($rView === 'plain' || $this->datatype->struct === 'plain') { + $this->indexAppendTable(); + } else { + $this->indexAppendTree(); + } + } + + public function indexSetMenu($rSid) + { + $types = []; + $canProceed = false; + foreach ($this->app->types as $k => $v) { + if (((is_countable($this->formats) ? count($this->formats) : 0) == 0 || in_array($k, $this->formats)) && $this->user->can('admin', $v)) { + $types[$k] = $v->title; + $canProceed = $canProceed || $rSid == $k; + } + } + + uasort($types, $this->__sortTypes(...)); + + $this->layout->assignKeyValueList('INNER.0.0', $types, $rSid); + return $canProceed; + } + + public function indexAppendTable() + { + $table = new \PXAdminTableObjects($this->datatype); + $table->addToParent('INNER.1.0'); + + $cId = $this->request->getCid(); + $table->showChildren('cid'); + + if ($cId && $table->has($cId)) { + $this->indexAppendChild($table->get($cId)); + } + } + + public function indexAppendChild($cidObject) + { + $format = $this->datatype->id; + $rqCid = $this->request->getCid(); + + $this->layout->setThreeColumns(); + $this->layout->setGetVarToSave('cid', $rqCid); + + $allowedChilds = $this->app->GetAllowedChildsKeys($format, $cidObject); + + if (count($allowedChilds)) { + foreach ($allowedChilds as $childFormat) { + if ($this->app->types[$childFormat]->struct == 'tree' && $this->request->GetVar($childFormat . '_view') != 'plain') { + $objects = new \PXAdminTreeObjects($childFormat, $rqCid, 'ByParent'); + + } else { + $objects = new \PXAdminTableObjects($childFormat, $rqCid, 'ByParent'); + } + + $objects->setControlParent($rqCid); + $objects->addToParent('INNER.2.0'); + } + + } else { + $this->layout->notSetAllowedChilds('INNER.2.0', $format, $rqCid); + } + } + + + public function indexAppendTree() + { + $objects = new \PXAdminAjaxTreeObjects($this->datatype); + $objects->addToParent('INNER.1.0'); + } + + public function adminPopup() + { + $this->datatype = $this->getDatatype(); + $object = $this->popupGetObject(); + + if (!$this->user->isAdmin() || empty($object)) { + return $this->layout->assignError('OUTER.CONTENT', 'Нет доступа'); + } + + $action = $this->request->getAction(); + + $this->layout->setGetVarToSave('id', $this->request->getId()); + $this->layout->setGetVarToSave('format', $this->datatype->id); + $this->layout->setGetVarToSave('action', $action); + + $counts = []; + $possibleFormats = $this->datatype->childTypes(); + + foreach ($this->datatype->allowedChildTypes($object) as $allowedFormat => $childType) { + $counts[$allowedFormat] = $this->db->getObjectsByParent($childType, null, $object['id'], DB_SELECT_COUNT); + } + + $this->db->LoadDirectoriesByType($this->datatype, $object); + + // warn about parent if need + if (!empty($this->datatype->parent) && empty($object['parent']) && $this->datatype->struct != 'tree') { + $parentDatatype = $this->app->types[$this->datatype->parent]; + + \PXDecorativeWidgetsCollection::addToCollection( + "Внимание! " . + "Объект типа '{$this->datatype->title}' может не быть создан вне контекста объекта '{$parentDatatype->title}'.
" . + "Рекомендуется не использовать данный функционал для создания объектов данного типа.", + 'PXAdminForm', + 'notices', + \PXAdminForm::NOTICES_CONTENT + ); + } + + $form = new \PXAdminForm($object, $this->datatype); + $form->setDisabledStatus($this->popupCheckAccess($object)); + $form->setAction($this->request->getAction()); + $form->setArea($this->getArea()); + $form->setChildren($possibleFormats, $counts); + $form->setLinks($this->popupLinks($object)); + $form->setTitle($this->popupSetTitle($object)); + $form->getForm(); + $form->setDisabledStatus(false); //release global NLAbstractHTMLForm lock! + } + + public function &getDatatype($varName = 'format') + { + $rFormat = $this->request->getVar($varName); + if (!isset($this->app->types[$rFormat])) { + FatalError('Undefined datatype ' . $rFormat); + } + return $this->app->types[$rFormat]; + } + + public function popupGetObject() + { + $rId = $this->request->getId(); + $object = []; + + if ($rId == 0) { + $object = $this->app->initContentObject($this->datatype->id); + $rObject = $this->request->getContentObject($this->datatype); + + foreach ($rObject as $k => $v) { + if (array_key_exists($k, $object) && is_null($object[$k])) { + $object[$k] = $v; + } + } + } else { + if (!sizeof($object = $this->db->getObjectById($this->datatype, $rId))) { + FatalError('Объект не существует !'); + } + + if (!$this->user->can('admin', $this->datatype, $object)) { + $object = []; + } + } + + if (isset($object['sys_owner'])) { + $tmp = $this->db->getObjectById($this->app->types['suser'], $object['sys_owner']); + $object['ownerlogin'] = $tmp['title'] ?? ''; + } + + return $object; + } + + public function popupCheckAccess($object) + { + if (!$this->user->can(['write', (($this->request->getId() > 0) ? 'modify' : 'add')], $this->datatype, $object)) { + return true; + } + } + + public function popupSetTitle($object) + { + $title = ''; + if ($this->request->getId() > 0) { + if (isset($object) && isset($object['title'])) { + $title = '«' . mb_substr(strip_tags((string) $object['title']), 0, 32) . '» — '; + } + $title .= ' Редактирование'; + } else { + $title = 'Добавление'; + } + $title .= ' объекта формата «' . strip_tags((string) $this->datatype->title) . '»'; + return $title; + } + + public function popupLinks($object) + { + $db = & $this->db; + $app = & $this->app; + $datatype = & $this->datatype; + $request = & $this->request; + $layout = & $this->layout; + + $linksParam = []; + + $refFilters = (array)$request->GetVar('filters', []); + foreach ($datatype->references as $k => $reference) { + if ($reference->hidden) { + continue; + } + $refDatatype = $app->types[$k]; + + $existingLinks = $db->getLinks($reference, $datatype->id, $object['id']); + + $onPage = $app->getProperty('LINKS_ON_PAGE', 10); + $currentPage = $request->getVar($k . '_page', 1); + $layout->setGetVarToSave($k . '_page', $currentPage); + + $onlyExistingLinks = $request->GetVar($k . '_exist', $reference->byDefault); + $layout->setGetVarToSave($k . '_exist', $onlyExistingLinks); + + $filteredWhere = $this->applyFiltersToWhere($refFilters, $refDatatype); + $trueCond = $db->TrueStatusString() . '=' . $db->TrueStatusString(); + $falseCond = $db->TrueStatusString() . '=' . $db->TrueStatusString(false); + + if ($onlyExistingLinks) { + if (is_countable($existingLinks) ? count($existingLinks) : 0) { + $existingLinksWhere = $k . '.id IN ( ' . join(',', array_keys($existingLinks)) . ' ) '; + } else { + $existingLinksWhere = $falseCond; + } + [$possibleLinks, $pagerCount, $overallCount] = $this->_getLinksData($refDatatype, $existingLinksWhere . $filteredWhere, $trueCond, $onPage, $currentPage); + } else { + $mainWhere = $trueCond; //No stupid NULL + + if (!empty($reference->filterFrom) && $reference->from == $k) { + $mainWhere = '(' . $db->parseWhereTemplate($reference->filterFrom, $object, $datatype) . ')'; + } + + if (!empty($reference->filterTo) && $reference->to == $k) { + $mainWhere = '(' . $db->parseWhereTemplate($reference->filterTo, $object, $datatype) . ')'; + } + + if (!empty($reference->restrictBy)) { + $mainWhere .= ' AND '; + $mainWhere .= $k . '.' . $reference->restrictBy . " = '" . $db->escapeString($object[$reference->restrictBy]) . "'"; + $mainWhere = '(' . $mainWhere . ')'; + } + [$possibleLinks, $pagerCount, $overallCount] = $this->_getLinksData($refDatatype, $mainWhere . $filteredWhere, $mainWhere, $onPage, $currentPage); + if ($datatype->id == $k) { + unset($possibleLinks[$object['id']]); + } + } + + $linksParam[$k] = [ + "reference" => $reference, + "formatTo" => $refDatatype, + "links" => $existingLinks, + "pLinks" => $possibleLinks, + "objectsOnPage" => $onPage, + "page" => $currentPage, + "count" => $pagerCount, + "fullCount" => $overallCount, + "onlyExistingLinks" => $onlyExistingLinks, + "filters" => $refFilters, + ]; + } + return $linksParam; + } + + public function _getLinksData($dType, $where, $whereAllCount, $onPage, $cPage) + { + $pLinks = $this->db->getObjectsByWhereLimited($dType, null, $where, $onPage, $onPage * ($cPage - 1)); + $pCount = $this->db->getObjectsByWhere($dType, null, $where, DB_SELECT_COUNT); + $oCount = $this->db->getObjectsByWhere($dType, null, $whereAllCount, DB_SELECT_COUNT); + + return [$pLinks, $pCount, $oCount]; + } + + public function adminAction() + { + $this->datatype = & $this->getDatatype(); + + $action = $this->_route(); + switch ($action) { + default: //FIXME: default main action it is right behaviour ? + case 'main': + $this->actionMain(); + break; + + case 'children': + $this->actionChildren(); + break; + + case 'remove': + $this->actionRemove(); + break; + + case 'directup': + $this->actionUpContentObject(); + break; + + case 'directdown': + $this->actionDownContentObject(); + break; + + case 'directmove': + $this->actionMoveContentObject(); + break; + + case 'directremove': + $this->actionRemoveDirect(); + break; + + case 'directstatus': + $this->actionChangeStatus(); + break; + + case 'clone': + $this->actionCloneContentObject(); + break; + + case 'links': + $this->actionModifyLinks(); + break; + + case 'auditlog': + //do nothing. FIXME: make actions configurable (like in multioperations module), not statically written here ! + break; + } + return $this->nextLocation; + } + + protected function getArea() + { + return 'objects'; + } + + public function _route() + { + $rAction = $this->request->getAction(); + $this->nextLocation = 'popup.phtml?area=' . $this->getArea() . '&format=' . $this->datatype->id . '&action=' . $rAction; + return $rAction; + } + + public function actionMain() + { + $object = $this->request->getContentObject($this->datatype); + + if ($object['id']) { + $this->db->modifyContentObject($this->datatype, $object, true); // Preserve values with displaytype HIDDEN or STATIC + $this->returnToId($object['id']); + + } else { + $id = $this->db->addContentObject($this->datatype, $object); + $this->returnToId($id); + } + } + + public function actionCloneContentObject() + { + $donor = $this->request->getContentObject($this->datatype); + + if (!is_numeric($donor['id']) || !$donor['id']) { + FatalError('Клонировать можно только существующие объекты'); + } + + $cloneId = $this->db->cloneContentObject($this->datatype, $donor['id']); + $this->returnToId($cloneId, 'main'); + } + + public function actionChildren() + { + if (!(is_countable($this->datatype->childs) ? count($this->datatype->childs) : 0)) { + FatalError("В этом разделе невозможно назначать потомков"); + } + + $object = $this->request->getObjectSysVars($this->datatype, [OBJ_FIELD_CHILDREN]); + + if ($object['id']) { + $this->db->modifyObjectSysVars($this->datatype, $object); + $this->returnToId($object['id']); + + } else { + $this->returnToReferer(); + } + } + + public function actionRemove() + { + if ($this->request->getAck()) { + $this->db->deleteContentObject($this->datatype, $this->request->getID()); + } + + closeAndRefresh(); + } + + public function actionRemoveDirect() + { + $this->db->deleteContentObject($this->datatype, $this->request->getId()); + $this->returnToReferer(); + } + + public function applyFiltersToUri($filters) + { + foreach ($filters as $dt => $fields) { + if (is_array($fields) && count($fields) > 0) { + foreach ($fields as $field => $filter) { + if (empty($filter)) { + continue; + } + $this->nextLocation .= '&filters[' . $dt . ']' . '[' . $field . ']=' . rawurlencode((string) $filter); + } + } + } + } + + public function applyFiltersToWhere($refFilters, $refDatatype) + { + $filterOnWhere = null; + if (isset($refFilters[$refDatatype->id]) && + is_array($refFilters[$refDatatype->id]) && + count($refFilters[$refDatatype->id]) > 0 + ) { + $applied = 0; + $filterOnWhere = ' AND ('; + foreach ($refFilters[$refDatatype->id] as $field => $filter) { + if (empty($filter)) { + continue; + } + $applied++; + $this->layout->setGetVarToSave('filters[' . $refDatatype->id . '][' . $field . ']', $filter); + //apply simple filter logic, ex. : ^search string$ + $modifiers = P_LEFT | P_RIGHT; + if (str_starts_with((string) $filter, '^')) { + $modifiers &= P_RIGHT; + $filter = substr((string) $filter, 1); + } + if (str_ends_with((string) $filter, '$')) { + $modifiers &= P_LEFT; + $filter = substr((string) $filter, 0, -1); + } + + // build where + $filterOnWhere .= $refDatatype->id . '.' . $this->db->escapeString($field) . $this->db->LIKE($filter, $modifiers) . " AND "; + } + + if (!$applied) { + $filterOnWhere = null; + } else { + $filterOnWhere = substr($filterOnWhere, 0, -4); //remove last AND or OR + $filterOnWhere .= ')'; + } + } + return $filterOnWhere; + } + + public function appendLinksExistFlag() + { + foreach ($this->request->GetAllPostData() as $k => $v) { + if (is_string($k) && strstr($k, '_exist') == '_exist') { + $this->nextLocation .= "&{$k}={$v}"; + } + } + } + + public function returnToId($id, $action = null) + { + $this->nextLocation .= '&id=' . $id; + + if (is_string($action)) { + $this->nextLocation .= '&action=' . $action; + } + } + + public function returnToReferer() + { + $this->nextLocation = $this->request->GetReferer(); + } + + public function actionUpContentObject() + { + $this->db->upContentObject($this->datatype, $this->request->getId()); + $this->returnToReferer(); + } + + public function actionDownContentObject() + { + $this->db->downContentObject($this->datatype, $this->request->getId()); + $this->returnToReferer(); + } + + public function actionMoveContentObject() + { + $this->db->moveContentObject($this->datatype, $this->request->getId(), $this->request->getVar('shift')); + $this->returnToReferer(); + } + + public function actionChangeStatus() + { + $object = $this->db->getObjectById($this->datatype, $this->request->getId()); + $object['status'] = !$object['status']; + $this->db->modifyContentObject($this->datatype, $object); + + $this->returnToReferer(); + } + + public function actionModifyLinks() + { + $id = $this->request->getId(); + foreach ($this->datatype->references as $reference) { + if ($reference->hidden) { + continue; + } + $ll = $this->_makeLinkedListFrom($reference); + $this->db->ModifyLinks($reference, $this->datatype->id, $id, $ll, false); + } + + if (count($filters = (array)$this->request->GetVar('filters', []))) { + $this->applyFiltersToUri($filters); + } + $this->appendLinksExistFlag(); + $this->returnToId($id); + } + + public function _makeLinkedListFrom($ref) + { + $linkedList = []; + foreach ($this->request->getLinks($ref) as $values) { + foreach ($values as $id => $data) { + $node = &$linkedList[$id]; + if (isset($linkedList[$id])) { + while (!is_null($node = &$node['next'])) ; + } + $node = $data; + $node['next'] = null; + } + } + return $linkedList; + } + + public function __sortTypes($a, $b) + { + return strcoll((string) $a, (string) $b); + } } diff --git a/src/Module/PropertiesModule.php b/src/Module/PropertiesModule.php index 967e3e3f..706f833b 100644 --- a/src/Module/PropertiesModule.php +++ b/src/Module/PropertiesModule.php @@ -16,549 +16,549 @@ */ class PropertiesModule extends AbstractModule { - - /** @var array */ - protected $predefinedPropertyDefList = []; - - /** - * {@inheritdoc} - */ - public function __construct($area, $settings, $selfDescription) - { - parent::__construct($area, $settings, $selfDescription); - - if (!empty($this->settings['attribute'])) { - $this->parsePredefinedProperties((array)$this->settings['attribute']); - } - } - - /** - * {@inheritdoc} - */ - public static function getAclModuleActions() - { - $defaults = parent::getAclModuleActions(); - $defaults['sys_properties_edit'] = \PXRegistry::getApp() - ->langTree - ->getByPath('module_macl_rules.actions.sys_properties_edit.rus'); - - return $defaults; - } - - /** - * {@inheritdoc} - */ - public function adminIndex() - { - // get sid - $sid = $this->request->getSid(); - $sid = (empty($sid)) ? 'pub' : $sid; - $sid = ($this->isPowerUser()) ? $sid : 'pub'; - - // build menu - $menu = []; - $menu['pub'] = $this->app->langTree - ->getByPath('module_properties.menu.general.rus'); - - if ($this->isPowerUser()) { - $menu['sys'] = $this->app->langTree - ->getByPath('module_properties.menu.system.rus'); - } - $this->layout - ->assignKeyValueList('INNER.0.0', $menu, $sid); - - // build table - $colsDef = [ - 'description' => $this->app->langTree->getByPath('module_properties.table.description.rus'), - 'name' => $this->app->langTree->getByPath('module_properties.table.name.rus'), - 'value' => $this->app->langTree->getByPath('module_properties.table.value.rus'), - ]; - - $context = new ContextUrlGenerator(); - $context->setCurrentModule($this->area); - $generator = new UrlGenerator($context); - $adminGenerator = $generator->getAdminGenerator(); - - $popupParams = [ - 'action' => 'edit', - 'id' => '', - ]; - - $table = (new \PXAdminTableSimple($colsDef)) - ->setTableId('properties') - ->showEven() - ->setNullText($this->app->langTree->getByPath('module_properties.table_ctrl.empty_value.rus')) - ->setData($this->getPropertyList($sid)) - ->setControls( - $adminGenerator->popupUrl($popupParams), - $this->app->langTree->getByPath('module_properties.table_ctrl.edit.rus'), - 'edit', - false, - true - ); - - if ($this->isPowerUser()) { - $actionParams = [ - 'action' => 'delete', - 'id' => '', - ]; - $table->setControls( - $adminGenerator->actionUrl($actionParams), - $this->app->langTree->getByPath('module_properties.table_ctrl.delete.rus'), - 'del', - true, - false - ); - $table->setDict('value', function ($row, $val) { - if (!is_numeric($row['id'])) { - return $val; - } - $typedef = $this->getTypeDescription($row); - if (isset($typedef->fields['value']->displayType)) { - $typedef->fields[$pseudoName = "properties[{$row['id']}]"] = $typedef->fields['value']; - $row[$pseudoName] = $val ?? ''; - $typedef->fields[$pseudoName]->name = $pseudoName; - $control = ''; - if ($typedef->fields['value']->displayType instanceof \PXDisplayTypeCheckbox) { - $control .= sprintf( - '', $row['id']); - } - $control .= $typedef->fields[$pseudoName]->displayType->buildInput( - $typedef->fields[$pseudoName], $row); - return $control; - } - return $val; - }); - $this->layout->append('INNER.1.0', - sprintf('
', $adminGenerator->actionUrl())); - - $this->layout->assignInlineCSS('.simpleform td input, .simpleform td textarea { width: 100% }'); - } - - $table->addToParent('INNER.1.0'); - - // build additional controls - if ($this->isPowerUser()) { - $saveAllButtonName = $this->app->langTree->getByPath('module_properties.table_ctrl.save_all.rus'); - $this->layout->append('INNER.1.0', <<settings['attribute'])) { + $this->parsePredefinedProperties((array)$this->settings['attribute']); + } + } + + /** + * {@inheritdoc} + */ + public static function getAclModuleActions() + { + $defaults = parent::getAclModuleActions(); + $defaults['sys_properties_edit'] = \PXRegistry::getApp() + ->langTree + ->getByPath('module_macl_rules.actions.sys_properties_edit.rus'); + + return $defaults; + } + + /** + * {@inheritdoc} + */ + public function adminIndex() + { + // get sid + $sid = $this->request->getSid(); + $sid = (empty($sid)) ? 'pub' : $sid; + $sid = ($this->isPowerUser()) ? $sid : 'pub'; + + // build menu + $menu = []; + $menu['pub'] = $this->app->langTree + ->getByPath('module_properties.menu.general.rus'); + + if ($this->isPowerUser()) { + $menu['sys'] = $this->app->langTree + ->getByPath('module_properties.menu.system.rus'); + } + $this->layout + ->assignKeyValueList('INNER.0.0', $menu, $sid); + + // build table + $colsDef = [ + 'description' => $this->app->langTree->getByPath('module_properties.table.description.rus'), + 'name' => $this->app->langTree->getByPath('module_properties.table.name.rus'), + 'value' => $this->app->langTree->getByPath('module_properties.table.value.rus'), + ]; + + $context = new ContextUrlGenerator(); + $context->setCurrentModule($this->area); + $generator = new UrlGenerator($context); + $adminGenerator = $generator->getAdminGenerator(); + + $popupParams = [ + 'action' => 'edit', + 'id' => '', + ]; + + $table = (new \PXAdminTableSimple($colsDef)) + ->setTableId('properties') + ->showEven() + ->setNullText($this->app->langTree->getByPath('module_properties.table_ctrl.empty_value.rus')) + ->setData($this->getPropertyList($sid)) + ->setControls( + $adminGenerator->popupUrl($popupParams), + $this->app->langTree->getByPath('module_properties.table_ctrl.edit.rus'), + 'edit', + false, + true + ); + + if ($this->isPowerUser()) { + $actionParams = [ + 'action' => 'delete', + 'id' => '', + ]; + $table->setControls( + $adminGenerator->actionUrl($actionParams), + $this->app->langTree->getByPath('module_properties.table_ctrl.delete.rus'), + 'del', + true, + false + ); + $table->setDict('value', function ($row, $val) { + if (!is_numeric($row['id'])) { + return $val; + } + $typedef = $this->getTypeDescription($row); + if (isset($typedef->fields['value']->displayType)) { + $typedef->fields[$pseudoName = "properties[{$row['id']}]"] = $typedef->fields['value']; + $row[$pseudoName] = $val ?? ''; + $typedef->fields[$pseudoName]->name = $pseudoName; + $control = ''; + if ($typedef->fields['value']->displayType instanceof \PXDisplayTypeCheckbox) { + $control .= sprintf( + '', + $row['id'] + ); + } + $control .= $typedef->fields[$pseudoName]->displayType->buildInput( + $typedef->fields[$pseudoName], + $row + ); + return $control; + } + return $val; + }); + $this->layout->append( + 'INNER.1.0', + sprintf('', $adminGenerator->actionUrl()) + ); + + $this->layout->assignInlineCSS('.simpleform td input, .simpleform td textarea { width: 100% }'); + } + + $table->addToParent('INNER.1.0'); + + // build additional controls + if ($this->isPowerUser()) { + $saveAllButtonName = $this->app->langTree->getByPath('module_properties.table_ctrl.save_all.rus'); + $this->layout->append( + 'INNER.1.0', + <<

MASS_ACTIONS - ); - - $link = sprintf("Popup('%s');", $adminGenerator->popupUrl(['action' => 'main'])); - $label = $this->app->langTree->getByPath('module_properties.table_ctrl.add.rus'); - - $button = (new \PXControlButton($label)) - ->setClickCode($link) - ->setClass('add'); - - $button->addToParent('INNER.1.1'); - } - } - - /** - * {@inheritdoc} - */ - public function adminPopup() - { - $id = $this->request->getId(); - - $propertyDef = $this->getPropertyDefById($id); - $typeDef = $this->getTypeDescription($propertyDef); - $form = new \PXAdminForm( - [ - 'id' => $propertyDef['id'], - 'sys_uuid' => $propertyDef['sys_uuid'], - 'name' => $propertyDef['name'], - 'description' => $propertyDef['description'], - 'value' => $propertyDef['value'], - ], $typeDef - ); - - $form->setAction('main'); - $form->setArea($this->area); - $form->setTitle($this->app->langTree->getByPath('module_properties.action.add.rus')); - $form->getForm(); - - $this->layout->assign('OUTER.MENU', ''); - } - - /** - * {@inheritdoc} - */ - public function adminAction() - { - $redirect = null; - - switch ($this->request->getAction()) { - case 'main': - $id = $this->request->getId(); - $redirect = $this->saveAction($id, $this->getPropertyFromRequest($id)); - break; - - case 'delete': - $id = $this->request->getId(); - $this->deleteAction($id, $this->getPropertyFromRequest($id)); - break; - case 'save_all': - $props = (array)$this->request->getVar('properties'); - $props_unchecked = (array)$this->request->getVar('properties_unchecked'); - - foreach ($props_unchecked as $id => $v) { - if (!array_key_exists($id, $props)) { - $props[$id] = null; - } - } - - if (!count($props)) { - return null; - } - - foreach ($props as $id => $value) { - $this->request->setVar('value', $value); - $prop = $this->getPropertyFromRequest($id); - $this->saveAction($id, ['value' => $prop['value']]); - } - - break; - } - - return $redirect; - } - - protected function getPropertyFromRequest($id) - { - $propertyDef = $this->getPropertyDefById($id); - $typeDef = $this->getTypeDescription($propertyDef); - return $this->request->getContentObject($typeDef); - } - - /** - * @param $id - * @return array|null - */ - protected function getPropertyDefById($id) - { - if (isset($this->predefinedPropertyDefList[$id])) { - $propertyDef = $this->predefinedPropertyDefList[$id]; - $propertyDef['id'] = $propertyDef['name']; - $propertyDef['sys_uuid'] = ''; - $propertyDef['value'] = ''; - - } else { - $propertyDef = PropertyLoader::getPropertyById($id, $this->db); - if ($propertyDef === null) { - $propertyDef = [ - 'id' => null, - 'sys_uuid' => '', - 'name' => '', - 'description' => '', - 'value' => '', - ]; - } - } - - // protect from non-power users.. - if (!$this->isPowerUser() && $this->isPropertySystem($propertyDef)) { - FatalError("Access denied"); - } - - return $propertyDef; - } - - /** - * Delete property by id. - * Public defined properties will be displayed anyway. - * - * @param mixed $id - * @param array $object - */ - protected function deleteAction($id, $object) - { - $id = is_numeric($id) ? $id : null; - if (empty($id)) { - return; - } - - $deleteQuery = sprintf("DELETE FROM %s WHERE id=%d", DT_PROPERTIES, $this->db->EscapeString($id)); - $this->db->ModifyingQuery($deleteQuery, DT_PROPERTIES); - } - - /** - * Update/Insert new property to database. - * Only power users can create new properties. - * - * @param mixed $id - * @param array $object - * @return string - */ - protected function saveAction($id, $object) - { - unset($object['id']); - if (empty($object['sys_uuid'])) { - $object['sys_uuid'] = Uuid::uuid4()->toString(); - } - - $fields = array_keys($object); - $values = array_values($object); - $id = is_numeric($id) ? $id : null; - - if (empty($id)) { - $id = $this->db->InsertObject(DT_PROPERTIES, $fields, $values); - } else { - $this->db->UpdateObjectById(DT_PROPERTIES, $id, $fields, $values); - } - - $context = new ContextUrlGenerator(); - $context->setCurrentModule($this->area); - $generator = new UrlGenerator($context); - - $popupParams = [ - 'action' => 'main', - 'id' => $id, - ]; - - return $generator->getAdminGenerator()->popupUrl($popupParams); - } - - /** - * @param array $publicProperties - */ - protected function parsePredefinedProperties(array $publicProperties) - { - foreach ($publicProperties as $propertyStrDef) { - $propertyDef = []; - $formatString = str_replace('|', '&', $propertyStrDef); - - parseStrMagic($formatString, $propertyDef); - if (!isset($propertyDef['name'])) { - FatalError('В параметрах модуля, для одного из полей отсутствует обязательный параметр name'); - } - - $propertyDef['description'] = isset($propertyDef['description']) - ? pp_simplexml_encode_string($propertyDef['description']) - : $propertyDef['name']; - - $propertyDef['displaytype'] = isset($propertyDef['displaytype']) - ? str_replace(',', '|', $propertyDef['displaytype']) - : 'TEXT'; - - $key = $propertyDef['name']; - $this->predefinedPropertyDefList[$key] = $propertyDef; - } - } - - /** - * Is current user allowed to modify system properties? - * - * @return bool - */ - protected function isPowerUser() - { - return $this->user->can('sys_properties_edit', $this->app->modules[$this->area]); - } - - /** - * @param array $propertyRaw - * @return bool - */ - protected function isPropertySystem(array $propertyRaw) - { - // public property - property listed in module settings and don't have SYS_/sys_ prefix - $key = $propertyRaw['name']; - $prefix = mb_strtolower(mb_substr($key, 0, mb_strlen('sys_'))); - - return ($prefix === 'sys_' || !isset($this->predefinedPropertyDefList[$key])); - } - - /** - * Get property list with mixin of predefined properties - * - * @param string $sid - * @return array - */ - protected function getPropertyList($sid) - { - $propertyList = PropertyLoader::getRawPropertyList($this->db); - - // filtering callable - $propertySidFilter = function ($propertyDef) use (&$sid) { - if ($sid === 'pub') { - return $this->isPropertySystem($propertyDef) === false; - } else { - return $this->isPropertySystem($propertyDef); - } - }; - - // filter property list based on sid - $propertyList = array_filter($propertyList, $propertySidFilter); - - // fetch and filter defined settings - $propertyListDef = array_filter($this->predefinedPropertyDefList, $propertySidFilter); - - // extract properties already present in database and defined in module settings - $createdDef = array_filter($propertyList, function ($propertyRaw) use (&$propertyListDef) { - return isset($propertyListDef[$propertyRaw['name']]); - }); - - $createdDef = array_flat($createdDef, 'name'); - $missingDef = array_diff_key($propertyListDef, $createdDef); - - // mixin defined, but not yet created properties - foreach ($missingDef as $propertyDef) { - $format = $this->getTypeDescription($propertyDef); - $table = $this->normalizeType($propertyDef, $format); - $propertyList = array_merge($table, $propertyList); - } - - // transform and mixin defined and created properties.. - $propertyList = array_map( - function ($propertyRaw) use (&$createdDef) { - $key = $propertyRaw['name']; - if (isset($createdDef[$key])) { - $propertyDef = $createdDef[$key]; - $format = $this->getTypeDescription($propertyDef); - $table = $this->normalizeType($propertyDef, $format); - return reset($table); - } - - return $propertyRaw; - }, - $propertyList - ); - - // resort all properties - usort($propertyList, function ($left, $right) { - return mb_strcasecmp($left['name'], $right['name']); - }); - - return $propertyList; - } - - /** - * Perform property normalization after database fetch. - * - * @param array $propertyRaw - * @param \PXTypeDescription $format - * @return array - */ - protected function normalizeType($propertyRaw, $format) - { - $table = [ - [ - 'id' => isset($propertyRaw['id']) ? $propertyRaw['id'] : $propertyRaw['name'], - 'sys_created' => null, - 'sys_modified' => null, - 'name' => $propertyRaw['name'], - 'description' => $propertyRaw['description'], - 'value' => isset($propertyRaw['value']) ? $propertyRaw['value'] : null, - ], - ]; - - $this->db->_NormalizeTable($table, $format, true); - return $table; - } - - /** - * @param array $object - * @return \PXTypeDescription - */ - protected function getTypeDescription($object) - { - $name = isset($object['name']) ? $object['name'] : null; - $displayTypeDef = []; - - // build predefined datatype? - if ($name !== null && isset($this->predefinedPropertyDefList[$name])) { - $displayTypeDef = $this->predefinedPropertyDefList[$name]; - } - - $objectDef = [ - 'id' => [ - 'name' => 'id', - 'description' => 'PK', - 'displaytype' => 'HIDDEN', - 'storagetype' => 'pk', - ], - OBJ_FIELD_UUID => [ - 'name' => 'sys_uuid', - 'description' => '', - 'displaytype' => 'HIDDEN', - 'storagetype' => 'string', - ], - 'name' => [ - 'name' => 'name', - 'description' => $this->app->langTree->getByPath('module_properties.table.name.rus'), - 'displaytype' => 'TEXT', - 'storagetype' => 'string', - ], - 'value' => [ - 'name' => 'value', - 'description' => $this->app->langTree->getByPath('module_properties.table.value.rus'), - 'displaytype' => isset($displayTypeDef['displaytype']) ? $displayTypeDef['displaytype'] : 'TEXT', - 'storagetype' => 'string', - ], - 'description' => [ - 'name' => 'description', - 'description' => $this->app->langTree->getByPath('module_properties.table.description.rus'), - 'displaytype' => 'TEXT|500|100', - 'storagetype' => 'string', - ], - ]; - - return $this->buildTypeFromArray($objectDef); - } - - /** - * @param $objectDef - * @return \PXTypeDescription - */ - protected function buildTypeFromArray($objectDef) - { - $typeDescription = new \PXTypeDescription(); - $typeDescription->id = 'sys_property'; - $typeDescription->title = $this->app->langTree->getByPath('module_properties.table.name.rus'); - - foreach ($objectDef as $dataDef) { - $_tmpSource = null; - if (isset($dataDef['source'], $this->app->directory[$dataDef['source']])) { - $_tmpSource = $dataDef['source']; - unset($dataDef['source']); - } - - $field = new \PXFieldDescription( - $this->createAttributeNode($dataDef), - $this->app, - $typeDescription - ); - - $field->listed = false; - if ($_tmpSource !== null) { - $field->source = $_tmpSource; - $field->values = &$this->app->directory[$_tmpSource]; - } - - $typeDescription->addField($field); - $typeDescription->assignToGroup($field); - } - - return $typeDescription; - } - - /** - * @param array $data - * @return SimpleXmlNode - */ - protected function createAttributeNode($data) - { - $attr = new \SimpleXMLElement(""); - foreach ($data as $k => $v) { - $attr->addAttribute($k, $v); - } - - return new SimpleXmlNode($attr); - } + ); + + $link = sprintf("Popup('%s');", $adminGenerator->popupUrl(['action' => 'main'])); + $label = $this->app->langTree->getByPath('module_properties.table_ctrl.add.rus'); + + $button = (new \PXControlButton($label)) + ->setClickCode($link) + ->setClass('add'); + + $button->addToParent('INNER.1.1'); + } + } + + /** + * {@inheritdoc} + */ + public function adminPopup() + { + $id = $this->request->getId(); + + $propertyDef = $this->getPropertyDefById($id); + $typeDef = $this->getTypeDescription($propertyDef); + $form = new \PXAdminForm( + [ + 'id' => $propertyDef['id'], + 'sys_uuid' => $propertyDef['sys_uuid'], + 'name' => $propertyDef['name'], + 'description' => $propertyDef['description'], + 'value' => $propertyDef['value'], + ], + $typeDef + ); + + $form->setAction('main'); + $form->setArea($this->area); + $form->setTitle($this->app->langTree->getByPath('module_properties.action.add.rus')); + $form->getForm(); + + $this->layout->assign('OUTER.MENU', ''); + } + + /** + * {@inheritdoc} + */ + public function adminAction() + { + $redirect = null; + + switch ($this->request->getAction()) { + case 'main': + $id = $this->request->getId(); + $redirect = $this->saveAction($id, $this->getPropertyFromRequest($id)); + break; + + case 'delete': + $id = $this->request->getId(); + $this->deleteAction($id, $this->getPropertyFromRequest($id)); + break; + case 'save_all': + $props = (array)$this->request->getVar('properties'); + $props_unchecked = (array)$this->request->getVar('properties_unchecked'); + + foreach ($props_unchecked as $id => $v) { + if (!array_key_exists($id, $props)) { + $props[$id] = null; + } + } + + if (!count($props)) { + return null; + } + + foreach ($props as $id => $value) { + $this->request->setVar('value', $value); + $prop = $this->getPropertyFromRequest($id); + $this->saveAction($id, ['value' => $prop['value']]); + } + + break; + } + + return $redirect; + } + + protected function getPropertyFromRequest($id) + { + $propertyDef = $this->getPropertyDefById($id); + $typeDef = $this->getTypeDescription($propertyDef); + return $this->request->getContentObject($typeDef); + } + + /** + * @param $id + * @return array|null + */ + protected function getPropertyDefById($id) + { + if (isset($this->predefinedPropertyDefList[$id])) { + $propertyDef = $this->predefinedPropertyDefList[$id]; + $propertyDef['id'] = $propertyDef['name']; + $propertyDef['sys_uuid'] = ''; + $propertyDef['value'] = ''; + + } else { + $propertyDef = PropertyLoader::getPropertyById($id, $this->db); + if ($propertyDef === null) { + $propertyDef = [ + 'id' => null, + 'sys_uuid' => '', + 'name' => '', + 'description' => '', + 'value' => '', + ]; + } + } + + // protect from non-power users.. + if (!$this->isPowerUser() && $this->isPropertySystem($propertyDef)) { + FatalError("Access denied"); + } + + return $propertyDef; + } + + /** + * Delete property by id. + * Public defined properties will be displayed anyway. + * + * @param array $object + */ + protected function deleteAction(mixed $id, $object) + { + $id = is_numeric($id) ? $id : null; + if (empty($id)) { + return; + } + + $deleteQuery = sprintf("DELETE FROM %s WHERE id=%d", DT_PROPERTIES, $this->db->EscapeString($id)); + $this->db->ModifyingQuery($deleteQuery, DT_PROPERTIES); + } + + /** + * Update/Insert new property to database. + * Only power users can create new properties. + * + * @param array $object + * @return string + */ + protected function saveAction(mixed $id, $object) + { + unset($object['id']); + if (empty($object['sys_uuid'])) { + $object['sys_uuid'] = Uuid::uuid4()->toString(); + } + + $fields = array_keys($object); + $values = array_values($object); + $id = is_numeric($id) ? $id : null; + + if (empty($id)) { + $id = $this->db->InsertObject(DT_PROPERTIES, $fields, $values); + } else { + $this->db->UpdateObjectById(DT_PROPERTIES, $id, $fields, $values); + } + + $context = new ContextUrlGenerator(); + $context->setCurrentModule($this->area); + $generator = new UrlGenerator($context); + + $popupParams = [ + 'action' => 'main', + 'id' => $id, + ]; + + return $generator->getAdminGenerator()->popupUrl($popupParams); + } + + protected function parsePredefinedProperties(array $publicProperties) + { + foreach ($publicProperties as $propertyStrDef) { + $propertyDef = []; + $formatString = str_replace('|', '&', (string) $propertyStrDef); + + parseStrMagic($formatString, $propertyDef); + if (!isset($propertyDef['name'])) { + FatalError('В параметрах модуля, для одного из полей отсутствует обязательный параметр name'); + } + + $propertyDef['description'] = isset($propertyDef['description']) + ? pp_simplexml_encode_string($propertyDef['description']) + : $propertyDef['name']; + + $propertyDef['displaytype'] = isset($propertyDef['displaytype']) + ? str_replace(',', '|', (string) $propertyDef['displaytype']) + : 'TEXT'; + + $key = $propertyDef['name']; + $this->predefinedPropertyDefList[$key] = $propertyDef; + } + } + + /** + * Is current user allowed to modify system properties? + * + * @return bool + */ + protected function isPowerUser() + { + return $this->user->can('sys_properties_edit', $this->app->modules[$this->area]); + } + + /** + * @return bool + */ + protected function isPropertySystem(array $propertyRaw) + { + // public property - property listed in module settings and don't have SYS_/sys_ prefix + $key = $propertyRaw['name']; + $prefix = mb_strtolower(mb_substr((string) $key, 0, mb_strlen('sys_'))); + + return ($prefix === 'sys_' || !isset($this->predefinedPropertyDefList[$key])); + } + + /** + * Get property list with mixin of predefined properties + * + * @param string $sid + * @return array + */ + protected function getPropertyList($sid) + { + $propertyList = PropertyLoader::getRawPropertyList($this->db); + + // filtering callable + $propertySidFilter = function ($propertyDef) use (&$sid) { + if ($sid === 'pub') { + return $this->isPropertySystem($propertyDef) === false; + } else { + return $this->isPropertySystem($propertyDef); + } + }; + + // filter property list based on sid + $propertyList = array_filter($propertyList, $propertySidFilter); + + // fetch and filter defined settings + $propertyListDef = array_filter($this->predefinedPropertyDefList, $propertySidFilter); + + // extract properties already present in database and defined in module settings + $createdDef = array_filter($propertyList, function ($propertyRaw) use (&$propertyListDef) { + return isset($propertyListDef[$propertyRaw['name']]); + }); + + $createdDef = array_flat($createdDef, 'name'); + $missingDef = array_diff_key($propertyListDef, $createdDef); + + // mixin defined, but not yet created properties + foreach ($missingDef as $propertyDef) { + $format = $this->getTypeDescription($propertyDef); + $table = $this->normalizeType($propertyDef, $format); + $propertyList = array_merge($table, $propertyList); + } + + // transform and mixin defined and created properties.. + $propertyList = array_map( + function ($propertyRaw) use (&$createdDef) { + $key = $propertyRaw['name']; + if (isset($createdDef[$key])) { + $propertyDef = $createdDef[$key]; + $format = $this->getTypeDescription($propertyDef); + $table = $this->normalizeType($propertyDef, $format); + return reset($table); + } + + return $propertyRaw; + }, + $propertyList + ); + + // resort all properties + usort($propertyList, fn ($left, $right) => mb_strcasecmp($left['name'], $right['name'])); + + return $propertyList; + } + + /** + * Perform property normalization after database fetch. + * + * @param array $propertyRaw + * @param \PXTypeDescription $format + * @return array + */ + protected function normalizeType($propertyRaw, $format) + { + $table = [ + [ + 'id' => $propertyRaw['id'] ?? $propertyRaw['name'], + 'sys_created' => null, + 'sys_modified' => null, + 'name' => $propertyRaw['name'], + 'description' => $propertyRaw['description'], + 'value' => $propertyRaw['value'] ?? null, + ], + ]; + + $this->db->_NormalizeTable($table, $format, true); + return $table; + } + + /** + * @param array $object + * @return \PXTypeDescription + */ + protected function getTypeDescription($object) + { + $name = $object['name'] ?? null; + $displayTypeDef = []; + + // build predefined datatype? + if ($name !== null && isset($this->predefinedPropertyDefList[$name])) { + $displayTypeDef = $this->predefinedPropertyDefList[$name]; + } + + $objectDef = [ + 'id' => [ + 'name' => 'id', + 'description' => 'PK', + 'displaytype' => 'HIDDEN', + 'storagetype' => 'pk', + ], + OBJ_FIELD_UUID => [ + 'name' => 'sys_uuid', + 'description' => '', + 'displaytype' => 'HIDDEN', + 'storagetype' => 'string', + ], + 'name' => [ + 'name' => 'name', + 'description' => $this->app->langTree->getByPath('module_properties.table.name.rus'), + 'displaytype' => 'TEXT', + 'storagetype' => 'string', + ], + 'value' => [ + 'name' => 'value', + 'description' => $this->app->langTree->getByPath('module_properties.table.value.rus'), + 'displaytype' => $displayTypeDef['displaytype'] ?? 'TEXT', + 'storagetype' => 'string', + ], + 'description' => [ + 'name' => 'description', + 'description' => $this->app->langTree->getByPath('module_properties.table.description.rus'), + 'displaytype' => 'TEXT|500|100', + 'storagetype' => 'string', + ], + ]; + + return $this->buildTypeFromArray($objectDef); + } + + /** + * @param $objectDef + * @return \PXTypeDescription + */ + protected function buildTypeFromArray($objectDef) + { + $typeDescription = new \PXTypeDescription(); + $typeDescription->id = 'sys_property'; + $typeDescription->title = $this->app->langTree->getByPath('module_properties.table.name.rus'); + + foreach ($objectDef as $dataDef) { + $_tmpSource = null; + if (isset($dataDef['source'], $this->app->directory[$dataDef['source']])) { + $_tmpSource = $dataDef['source']; + unset($dataDef['source']); + } + + $field = new \PXFieldDescription( + $this->createAttributeNode($dataDef), + $this->app, + $typeDescription + ); + + $field->listed = false; + if ($_tmpSource !== null) { + $field->source = $_tmpSource; + $field->values = &$this->app->directory[$_tmpSource]; + } + + $typeDescription->addField($field); + $typeDescription->assignToGroup($field); + } + + return $typeDescription; + } + + /** + * @param array $data + * @return SimpleXmlNode + */ + protected function createAttributeNode($data) + { + $attr = new \SimpleXMLElement(""); + foreach ($data as $k => $v) { + $attr->addAttribute($k, $v); + } + + return new SimpleXmlNode($attr); + } } diff --git a/src/Module/RssEngineModule.php b/src/Module/RssEngineModule.php index ad75dbe6..dacd7c08 100644 --- a/src/Module/RssEngineModule.php +++ b/src/Module/RssEngineModule.php @@ -12,7 +12,6 @@ */ class RssEngineModule extends AbstractModule { - public function __construct($area, $settings) { parent::__construct($area, $settings); @@ -25,7 +24,7 @@ public function _GetItems() return $this->items; } - public function userIndex() + public function userIndex(): void { FatalError('It\'s interface method. ReWrite it.'); exit; @@ -56,7 +55,7 @@ public function date2time($string) $time = mktime(0, 0, 0, date('n'), 1); } elseif ($string != '') { - preg_match("/^(\d{2})\.(\d{2})\.(\d{4})\s+(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?$/si", trim($string), $date); + preg_match("/^(\d{2})\.(\d{2})\.(\d{4})\s+(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?$/si", trim((string) $string), $date); $time = mktime($date[4], $date[5], $date[6], $date[2], $date[1], $date[3]); } else { @@ -71,14 +70,14 @@ public function rssDateFormat($string) return date('d M Y H:i:s O', $this->date2time($string)); } - public function GetXML($channel, $items) + public function GetXML($channel, $items): void { $lastBuildDate = reset($items); $channel['lastBuildDate'] = $this->rssDateFormat($lastBuildDate['pubDate']); $xml = $this->xml($channel, $items); - $xml = preg_replace("/Ничего не найдено'); - } - } - } + if (!$allCount) { + $this->layout->append('INNER.1.0', '

Ничего не найдено

'); + } + } + } - public function __sortTypes($a, $b) - { - return strcmp($a->title, $b->title); - } + public function __sortTypes($a, $b) + { + return strcmp((string) $a->title, (string) $b->title); + } - public function searchForm() - { - $datatypesHTML = '
    '; + public function searchForm() + { + $datatypesHTML = '
      '; - $types = $this->app->types; - uasort($types, [$this, '__sortTypes']); + $types = $this->app->types; + uasort($types, $this->__sortTypes(...)); - $checkedTypes = $this->request->getVar('d', []); + $checkedTypes = $this->request->getVar('d', []); - foreach ($types as $datatype) { - $checked = !sizeof($checkedTypes) || isset($checkedTypes[$datatype->id]) ? 'checked' : ''; - $datatypesHTML .= <<id]) ? 'checked' : ''; + $datatypesHTML .= << HTML; - } + } - $datatypesHTML .= '
    '; + $datatypesHTML .= '
'; - $hword = quot($this->word); - $html = <<word); + $html = << fieldset li, fieldset ul { padding: 0; @@ -93,25 +92,25 @@ public function searchForm() HTML; - $this->layout->assign('INNER.0.0', $html); - $this->layout->setGetVarToSave('word', $this->word); + $this->layout->assign('INNER.0.0', $html); + $this->layout->setGetVarToSave('word', $this->word); - // build the string for datatypes check - foreach ($checkedTypes as $k => $v) { - $this->layout->setGetVarToSave('d[' . $k . ']', 'on'); - } - } + // build the string for datatypes check + foreach ($checkedTypes as $k => $v) { + $this->layout->setGetVarToSave('d[' . $k . ']', 'on'); + } + } - public function find(&$datatype) - { - $count = $this->db->getObjectsBySearchWord($datatype, NULL, $this->word, DB_SELECT_COUNT); + public function find(&$datatype) + { + $count = $this->db->getObjectsBySearchWord($datatype, null, $this->word, DB_SELECT_COUNT); - if ($count) { - $table = new \PXAdminTableObjects($datatype, $this->word, 'BySearchWord'); - $table->addToParent('INNER.1.0'); - } + if ($count) { + $table = new \PXAdminTableObjects($datatype, $this->word, 'BySearchWord'); + $table->addToParent('INNER.1.0'); + } - return $count; - } + return $count; + } } diff --git a/src/Plugin/AbstractPlugin.php b/src/Plugin/AbstractPlugin.php index 0fe00fd3..5bc6c7f5 100644 --- a/src/Plugin/AbstractPlugin.php +++ b/src/Plugin/AbstractPlugin.php @@ -10,102 +10,96 @@ */ class AbstractPlugin { - - protected $name = null; - - public $description; - - /** @var \PXApplication */ - public $app; - - public function __construct($app, $description) - { - $this->app = $app; - $this->description = $description; - - $this->name = $description->getName(); - $this->path = dirname($this->description->getPathToPlugin()); - - array_map([$this, "loadModule"], $this->description->modules); - array_map([$this, "loadTrigger"], $this->description->triggers); - - $this->initialize($app); - } - - public function initialize($app) - { - } - - public function initSet($params = null) - { - } - - public static function getParam($pluginName, $paramName) - { - return @\PXRegistry::getApp()->plugins[$pluginName]->params[$paramName]; - } - - public function loadTrigger($relativePath) - { - [$type, $name] = explode("/", $relativePath); - $this->app->registerTrigger($type, ["name" => $name] + ["folder" => $this->name]); - } - - public function load($path, $pattern = "%s") - { - /** @noinspection PhpFormatFunctionParametersMismatchInspection */ - require_once sprintf("%s/{$pattern}", $this->path, $path); - } - - public function loadWithLoader($folder, $classPrefix, $filename_without_ext, $extension = 'class.inc') - { - \PXLoader::getInstance("{$this->path}/{$folder}/") - ->load("{$classPrefix}{$filename_without_ext}", "{$filename_without_ext}.{$extension}"); - } - - - public function loadModule($relativePath) - { - $this->load($relativePath, "modules/%s.module.inc"); - } - - public function loadCronrun($relativePath) - { - $this->load($relativePath, "cronruns/%s.cronrun.inc"); - } - - public function loadDisplayType($filename_without_ext) - { - $this->loadWithLoader('displayTypes', 'PXDisplayType', $filename_without_ext); - } - - public function loadStorageType($filename_without_ext) - { - $this->loadWithLoader('storageTypes', 'PXStorageType', $filename_without_ext); - } - - public function loadOnlyInAdmin($path) - { - if ($this->app->isAdminEngine()) { - $this->load($path); - } - } - - // what the hell is that? - public static function autoload($className) - { - $f = \PXLoader::find($className); - - if (!strstr($f, "/plugins/")) { - return; - } - - if (file_exists($f)) { - require_once $f; - - } else { - @unlinkDir(CACHE_PATH . "/config"); - @unlink(CACHE_PATH . "/loader"); - } - } + protected $name = null; + + /** + * @param \PXApplication $app + */ + public function __construct(public $app, public $description) + { + $this->name = $description->getName(); + $this->path = dirname((string) $this->description->getPathToPlugin()); + + array_map($this->loadModule(...), $this->description->modules); + array_map($this->loadTrigger(...), $this->description->triggers); + + $this->initialize($app); + } + + public function initialize($app) + { + } + + public function initSet($params = null) + { + } + + public static function getParam($pluginName, $paramName) + { + return @\PXRegistry::getApp()->plugins[$pluginName]->params[$paramName]; + } + + public function loadTrigger($relativePath) + { + [$type, $name] = explode("/", (string) $relativePath); + $this->app->registerTrigger($type, ["name" => $name] + ["folder" => $this->name]); + } + + public function load($path, $pattern = "%s") + { + /** @noinspection PhpFormatFunctionParametersMismatchInspection */ + require_once sprintf("%s/{$pattern}", $this->path, $path); + } + + public function loadWithLoader($folder, $classPrefix, $filename_without_ext, $extension = 'class.inc') + { + \PXLoader::getInstance("{$this->path}/{$folder}/") + ->load("{$classPrefix}{$filename_without_ext}", "{$filename_without_ext}.{$extension}"); + } + + + public function loadModule($relativePath) + { + $this->load($relativePath, "modules/%s.module.inc"); + } + + public function loadCronrun($relativePath) + { + $this->load($relativePath, "cronruns/%s.cronrun.inc"); + } + + public function loadDisplayType($filename_without_ext) + { + $this->loadWithLoader('displayTypes', 'PXDisplayType', $filename_without_ext); + } + + public function loadStorageType($filename_without_ext) + { + $this->loadWithLoader('storageTypes', 'PXStorageType', $filename_without_ext); + } + + public function loadOnlyInAdmin($path) + { + if ($this->app->isAdminEngine()) { + $this->load($path); + } + } + + // what the hell is that? + public static function autoload($className) + { + $f = \PXLoader::find($className); + + if (!strstr((string) $f, "/plugins/")) { + return; + } + + if (file_exists($f)) { + require_once $f; + + } else { + @unlinkDir(CACHE_PATH . "/config"); + @unlink(CACHE_PATH . "/loader"); + } + } } diff --git a/src/Plugin/SortableAcl/Sortable.php b/src/Plugin/SortableAcl/Sortable.php index 71b80a1c..d4e8d6ca 100644 --- a/src/Plugin/SortableAcl/Sortable.php +++ b/src/Plugin/SortableAcl/Sortable.php @@ -4,52 +4,58 @@ trait Sortable { - - public function addNewRuleButton() - { - $this->js(); - return parent::addNewRuleButton(); - } - - public function sort() - { - @parse_str($this->request->getVar('sorts'), $sorts); - - if (empty($sorts)) return; - - $this->db->transactionBegin(); - - foreach ($sorts as $order => $id) { - $this->db->modifyingQuery(sprintf("UPDATE %s SET sys_order = '%s' WHERE id = '%s'", - $this->sqlTable, $this->db->escapeString($order), - $this->db->escapeString($id))); - } - - $this->db->transactionCommit(); - - if ($this->request->isXmlHttpRequest()) die('ok'); - } - - public function adminAction() - { - if ($this->request->getVar('action') == 'sort') { - $this->sort(); - } - - return parent::adminAction(); - } - - public function js() - { - $options = [ - 'area' => $this->area, - 'sid' => $this->_getSid(), - ]; - - $this->layout->assignJs("js/tools/jui.min.js"); - $this->layout->assignInlineJs('var sortableaclOptions = ' . json_encode($options) . ';'); - $this->layout->assignInlineJs( - <<js(); + return parent::addNewRuleButton(); + } + + public function sort() + { + @parse_str($this->request->getVar('sorts'), $sorts); + + if (empty($sorts)) { + return; + } + + $this->db->transactionBegin(); + + foreach ($sorts as $order => $id) { + $this->db->modifyingQuery(sprintf( + "UPDATE %s SET sys_order = '%s' WHERE id = '%s'", + $this->sqlTable, + $this->db->escapeString($order), + $this->db->escapeString($id) + )); + } + + $this->db->transactionCommit(); + + if ($this->request->isXmlHttpRequest()) { + die('ok'); + } + } + + public function adminAction() + { + if ($this->request->getVar('action') == 'sort') { + $this->sort(); + } + + return parent::adminAction(); + } + + public function js() + { + $options = [ + 'area' => $this->area, + 'sid' => $this->_getSid(), + ]; + + $this->layout->assignJs("js/tools/jui.min.js"); + $this->layout->assignInlineJs('var sortableaclOptions = ' . json_encode($options) . ';'); + $this->layout->assignInlineJs( + <<path = $path; - $this->file = $file; - - $this->errors = []; - $this->required = []; - } - - /** - * Perform ENV injection - * it's safe to call it multiple times - */ - public static function inject() { - (new EnvLoader(BASEPATH)) - ->addRequired(['DATABASE_DSN']) - ->load(); - } - - /** - * Add key or list of keys to be required in environment, - * otherwise, exception will be raised. - * - * @param string|string[] $key - * @return $this - */ - public function addRequired($key) { - if (!is_array($key)) { - $key = [$key]; - } - - foreach ($key as $item) { - $this->required[$item] = static::TYPE_NOT_EMPTY; - } - - return $this; - } - - /** - * Load environment variables - * - * @throws EnvLoaderException - */ - public function load() { - $envFile = join(DIRECTORY_SEPARATOR, [rtrim($this->path, DIRECTORY_SEPARATOR), $this->file]); - if (file_exists($envFile)) { - $dotenv = new Dotenv($this->path, $this->file); - $dotenv->overload(); - } - - $this->validate(); - if (!$this->valid) { - throw new EnvLoaderException(join(', ', $this->errors)); - } - } - - /** - * Build array from list of provided keys - * - * @param string[] $mappings - * @return array - */ - public static function getMappedArray($mappings) { - $result = []; - - foreach ($mappings as $key => $mapped) { - if (is_int($key)) { // list is here.. - $key = $mapped; - $mapped = null; - } - - if (isset($_ENV[$key])) { - if ($mapped === null) { - $result[$key] = $_ENV[$key]; - } else { - $result[$mapped] = $_ENV[$key]; - } - } - } - - return $result; - } - - /** - * Return single value of key. - * - * @param string $key - * @return string - * @throws EnvLoaderException - */ - public static function get($key) { - if (isset($_ENV[$key])) { - return $_ENV[$key]; - } - - return null; - } - - /** - * Just validation loop - */ - protected function validate() { - $validationResult = true; - - foreach ($this->required as $key => $flags) { - switch (true) { - case ($flags & static::TYPE_NOT_EMPTY) == static::TYPE_NOT_EMPTY: - $validationResult = $validationResult && $this->isNotEmpty($key); - break; - } - } - - $this->valid = $validationResult; - } - - /** - * @param string $key - * @return bool - */ - protected function isNotEmpty($key) { - $res = (isset($_ENV[$key])) && (strlen($_ENV[$key]) > 0); - - if (!$res) { - $this->errors[] = "${key} should not be empty"; - } - - return $res; - } +class EnvLoader +{ + public const TYPE_NOT_EMPTY = 0; // default + + /** @var string[] */ + protected $errors; + + /** @var string[int] */ + protected $required; + + /** @var bool */ + protected $valid; + + /** + * EnvLoader constructor. + * + * @param string $path + * @param string $file + */ + public function __construct(protected $path, protected $file = '.env') + { + $this->errors = []; + $this->required = []; + } + + /** + * Perform ENV injection + * it's safe to call it multiple times + */ + public static function inject() + { + (new EnvLoader(BASEPATH)) + ->addRequired(['DATABASE_DSN']) + ->load(); + } + + /** + * Add key or list of keys to be required in environment, + * otherwise, exception will be raised. + * + * @param string|string[] $key + * @return $this + */ + public function addRequired(string|array $key) + { + if (!is_array($key)) { + $key = [$key]; + } + + foreach ($key as $item) { + $this->required[$item] = static::TYPE_NOT_EMPTY; + } + + return $this; + } + + /** + * Load environment variables + * + * @throws EnvLoaderException + */ + public function load() + { + $envFile = join(DIRECTORY_SEPARATOR, [rtrim($this->path, DIRECTORY_SEPARATOR), $this->file]); + if (file_exists($envFile)) { + $dotenv = new Dotenv($this->path, $this->file); + $dotenv->overload(); + } + + $this->validate(); + if (!$this->valid) { + throw new EnvLoaderException(join(', ', $this->errors)); + } + } + + /** + * Build array from list of provided keys + * + * @param string[] $mappings + * @return array + */ + public static function getMappedArray($mappings) + { + $result = []; + + foreach ($mappings as $key => $mapped) { + if (is_int($key)) { // list is here.. + $key = $mapped; + $mapped = null; + } + + if (isset($_ENV[$key])) { + if ($mapped === null) { + $result[$key] = $_ENV[$key]; + } else { + $result[$mapped] = $_ENV[$key]; + } + } + } + + return $result; + } + + /** + * Return single value of key. + * + * @param string $key + * @return string + * @throws EnvLoaderException + */ + public static function get($key) + { + if (isset($_ENV[$key])) { + return $_ENV[$key]; + } + + return null; + } + + /** + * Just validation loop + */ + protected function validate() + { + $validationResult = true; + + foreach ($this->required as $key => $flags) { + switch (true) { + case ($flags & static::TYPE_NOT_EMPTY) == static::TYPE_NOT_EMPTY: + $validationResult = $validationResult && $this->isNotEmpty($key); + break; + } + } + + $this->valid = $validationResult; + } + + /** + * @param string $key + * @return bool + */ + protected function isNotEmpty($key) + { + $res = (isset($_ENV[$key])) && (strlen((string) $_ENV[$key]) > 0); + + if (!$res) { + $this->errors[] = "${key} should not be empty"; + } + + return $res; + } } diff --git a/src/Properties/EnvLoaderException.php b/src/Properties/EnvLoaderException.php index 2927aebc..78591342 100644 --- a/src/Properties/EnvLoaderException.php +++ b/src/Properties/EnvLoaderException.php @@ -6,5 +6,6 @@ * Class EnvLoaderException * @package PP\Properties */ -class EnvLoaderException extends \Exception { +class EnvLoaderException extends \Exception +{ } diff --git a/src/Properties/PropertyLoader.php b/src/Properties/PropertyLoader.php index 99bd9b84..fb30bf87 100644 --- a/src/Properties/PropertyLoader.php +++ b/src/Properties/PropertyLoader.php @@ -8,61 +8,61 @@ * Class PropertyLoader * @package PP\Properties */ -class PropertyLoader { +class PropertyLoader +{ + /** + * Load all properties as key => value. + * + * @return array + * @internal should be used only in PXApplication context + */ + public static function getPropertyList(\PXDatabase|\PP\Lib\Database\Driver\PostgreSqlDriver $database) + { + $loadSql = sprintf( + 'SELECT "name", "value" FROM %s', + DT_PROPERTIES + ); - /** - * Load all properties as key => value. - * - * @param \PXDatabase|PostgreSqlDriver $database - * @return array - * @internal should be used only in PXApplication context - */ - public static function getPropertyList($database) { - $loadSql = sprintf( - 'SELECT "name", "value" FROM %s', - DT_PROPERTIES - ); + $propertyListRaw = $database->Query($loadSql); + return array_flat($propertyListRaw, 'name', 'value'); + } - $propertyListRaw = $database->Query($loadSql); - return array_flat($propertyListRaw, 'name', 'value'); - } + /** + * Load raw property list. + * + * @return array + * @internal should be used only in properties.module.inc + */ + public static function getRawPropertyList(\PXDatabase|\PP\Lib\Database\Driver\PostgreSqlDriver $database) + { + $loadSql = sprintf( + 'SELECT id, "name", description, "value", sys_uuid FROM %s ORDER BY "name"', + DT_PROPERTIES + ); - /** - * Load raw property list. - * - * @param \PXDatabase|PostgreSqlDriver $database - * @return array - * @internal should be used only in properties.module.inc - */ - public static function getRawPropertyList($database) { - $loadSql = sprintf( - 'SELECT id, "name", description, "value", sys_uuid FROM %s ORDER BY "name"', - DT_PROPERTIES - ); + return $database->Query($loadSql, true); + } - return $database->Query($loadSql, true); - } + /** + * Fetch property by id + * + * @param int $id + * @return array|null + * @internal should be used only in properties.module.inc + */ + public static function getPropertyById($id, \PXDatabase|\PP\Lib\Database\Driver\PostgreSqlDriver $database) + { + if (empty($id)) { + return null; + } - /** - * Fetch property by id - * - * @param int $id - * @param \PXDatabase|PostgreSqlDriver $database - * @return array|null - * @internal should be used only in properties.module.inc - */ - public static function getPropertyById($id, $database) { - if (empty($id)) { - return null; - } + $loadSql = sprintf( + 'SELECT id, "name", description, "value", sys_uuid FROM %s WHERE id=%d', + DT_PROPERTIES, + $database->EscapeString($id) + ); - $loadSql = sprintf( - 'SELECT id, "name", description, "value", sys_uuid FROM %s WHERE id=%d', - DT_PROPERTIES, - $database->EscapeString($id) - ); - - $rows = $database->Query($loadSql, true); - return $rows? reset($rows) : null; - } + $rows = $database->Query($loadSql, true); + return $rows ? reset($rows) : null; + } } diff --git a/src/Serializer/DefaultSerializer.php b/src/Serializer/DefaultSerializer.php index 60f4bc2b..a90c7f40 100644 --- a/src/Serializer/DefaultSerializer.php +++ b/src/Serializer/DefaultSerializer.php @@ -7,36 +7,40 @@ * * @package PP\Serializer */ -class DefaultSerializer implements SerializerInterface { +class DefaultSerializer implements SerializerInterface +{ + /** + * @return string + */ + public function getName() + { + return 'default'; + } - /** - * @return string - */ - public function getName() { - return 'default'; - } + /** + * @return bool + */ + public function isSupported() + { + return true; + } - /** - * @return bool - */ - public function isSupported() { - return true; - } + /** + * @param $data + * @return string + */ + public function serialize($data) + { + return serialize($data); + } - /** - * @param $data - * @return string - */ - public function serialize($data) { - return serialize($data); - } - - /** - * @param $data - * @return mixed - */ - public function unserialize($data) { - return @unserialize($data); - } + /** + * @param $data + * @return mixed + */ + public function unserialize($data) + { + return @unserialize($data); + } } diff --git a/src/Serializer/IgbinarySerializer.php b/src/Serializer/IgbinarySerializer.php index d6f65241..d399c871 100644 --- a/src/Serializer/IgbinarySerializer.php +++ b/src/Serializer/IgbinarySerializer.php @@ -7,36 +7,40 @@ * * @package PP\Serializer */ -class IgbinarySerializer implements SerializerInterface { +class IgbinarySerializer implements SerializerInterface +{ + /** + * @return string + */ + public function getName() + { + return 'igbinary'; + } - /** - * @return string - */ - public function getName() { - return 'igbinary'; - } + /** + * @return bool + */ + public function isSupported() + { + return extension_loaded('igbinary'); + } - /** - * @return bool - */ - public function isSupported() { - return extension_loaded('igbinary'); - } + /** + * @param $data + * @return string + */ + public function serialize($data) + { + return igbinary_serialize($data); + } - /** - * @param $data - * @return string - */ - public function serialize($data) { - return igbinary_serialize($data); - } - - /** - * @param $data - * @return mixed - */ - public function unserialize($data) { - return @igbinary_unserialize($data); - } + /** + * @param $data + * @return mixed + */ + public function unserialize($data) + { + return @igbinary_unserialize($data); + } } diff --git a/src/Serializer/SerializerAwareInterface.php b/src/Serializer/SerializerAwareInterface.php index 869d0200..e893ca77 100644 --- a/src/Serializer/SerializerAwareInterface.php +++ b/src/Serializer/SerializerAwareInterface.php @@ -7,11 +7,8 @@ * * @package PP\Serializer */ -interface SerializerAwareInterface { - - /** - * @param SerializerInterface $serializer - */ - public function setSerializer(SerializerInterface $serializer); +interface SerializerAwareInterface +{ + public function setSerializer(SerializerInterface $serializer); } diff --git a/src/Serializer/SerializerAwareTrait.php b/src/Serializer/SerializerAwareTrait.php index 54380ffa..20f47008 100644 --- a/src/Serializer/SerializerAwareTrait.php +++ b/src/Serializer/SerializerAwareTrait.php @@ -7,17 +7,15 @@ * * @package PP\Serializer */ -trait SerializerAwareTrait { +trait SerializerAwareTrait +{ + /** + * @var SerializerInterface + */ + protected $serializer; - /** - * @var SerializerInterface - */ - protected $serializer; - - /** - * @param SerializerInterface $serializer - */ - public function setSerializer(SerializerInterface $serializer) { - $this->serializer = $serializer; - } + public function setSerializer(SerializerInterface $serializer) + { + $this->serializer = $serializer; + } } diff --git a/src/Serializer/SerializerFactory.php b/src/Serializer/SerializerFactory.php index 1ff5795d..0dc7a930 100644 --- a/src/Serializer/SerializerFactory.php +++ b/src/Serializer/SerializerFactory.php @@ -7,27 +7,28 @@ * * @package PP\Serializer */ -class SerializerFactory { +class SerializerFactory +{ + /** + * @param $driver + * @return SerializerInterface + */ + public static function create($driver) + { + $driver = ucfirst(strtolower((string) $driver)); + $class = 'PP\Serializer\\' . $driver . 'Serializer'; - /** - * @param $driver - * @return SerializerInterface - */ - public static function create($driver) { - $driver = ucfirst(strtolower($driver)); - $class = 'PP\Serializer\\' . $driver . 'Serializer'; + if (!class_exists($class)) { + FatalError("Serializer class {$class} not found"); + } - if (!class_exists($class)) { - FatalError("Serializer class {$class} not found"); - } + /** @var SerializerInterface $instance */ + $instance = new $class(); + if (!$instance->isSupported()) { + FatalError("Serializer instance of {$class} is not supported"); + } - /** @var SerializerInterface $instance */ - $instance = new $class(); - if (!$instance->isSupported()) { - FatalError("Serializer instance of {$class} is not supported"); - } - - return $instance; - } + return $instance; + } } diff --git a/src/Serializer/SerializerInterface.php b/src/Serializer/SerializerInterface.php index 34d804fe..511160bd 100644 --- a/src/Serializer/SerializerInterface.php +++ b/src/Serializer/SerializerInterface.php @@ -7,27 +7,27 @@ * * @package PP\Serializer */ -interface SerializerInterface { +interface SerializerInterface +{ + /** + * @return string + */ + public function getName(); - /** - * @return string - */ - public function getName(); + /** + * @return bool + */ + public function isSupported(); - /** - * @return bool - */ - public function isSupported(); + /** + * @param $data + * @return string + */ + public function serialize($data); - /** - * @param $data - * @return string - */ - public function serialize($data); - - /** - * @param $data - * @return mixed - */ - public function unserialize($data); + /** + * @param $data + * @return mixed + */ + public function unserialize($data); } diff --git a/tests/Base/AbstractApplicationTest.php b/tests/Base/AbstractApplicationTest.php index a7648655..6b3e99b6 100644 --- a/tests/Base/AbstractApplicationTest.php +++ b/tests/Base/AbstractApplicationTest.php @@ -2,15 +2,14 @@ namespace Tests\Base; +use PHPUnit\Framework\TestCase as TCase; + /** * Class AbstractApplicationTest * @package Tests\Base */ -abstract class AbstractApplicationTest extends \PHPUnit_Framework_TestCase { +abstract class AbstractApplicationTest extends TCase { protected $engine; - protected function setUp() { - } - } diff --git a/tests/Unit/PP/CollectionTest.php b/tests/Unit/PP/CollectionTest.php index 0ff5be41..92a48de1 100644 --- a/tests/Unit/PP/CollectionTest.php +++ b/tests/Unit/PP/CollectionTest.php @@ -23,8 +23,8 @@ public function testJsonSerializeEqualsToArray() { $collection->push(1)->push(2); $this->assertJsonStringEqualsJsonString( - json_encode($collection->toArray()), - json_encode($collection) + json_encode($collection->toArray(), JSON_THROW_ON_ERROR), + json_encode($collection, JSON_THROW_ON_ERROR) ); } diff --git a/tests/Unit/PP/Lib/PersistentQueue/JobTest.php b/tests/Unit/PP/Lib/PersistentQueue/JobTest.php index 7b7413a3..8465ad06 100644 --- a/tests/Unit/PP/Lib/PersistentQueue/JobTest.php +++ b/tests/Unit/PP/Lib/PersistentQueue/JobTest.php @@ -13,36 +13,33 @@ public function testInitialStateFresh() { $this->assertEquals(Job::STATE_FRESH, $job->getState()); } - /** - * @expectedException UnexpectedValueException - * @expectedExceptionMessageRegExp /State is invalid/ - */ public function testSetInvalidState() { + $this->expectExceptionMessageMatches("/State is invalid/"); + $this->expectException(UnexpectedValueException::class); $job = new Job(); $job->setState('INVALID STATE'); } - /** - * @expectedException UnexpectedValueException - * @expectedExceptionMessageRegExp /Worker class does not exist/ - */ public function testFromArrayWorkerClassExists() { + $this->expectExceptionMessageMatches("/Worker class does not exist/"); + $this->expectException(UnexpectedValueException::class); + $arrayJob = ['worker' => 'PP\\SomeInexistentClass']; - Job::fromArray($arrayJob); + $job = Job::fromArray($arrayJob); + $job->getWorker(); } - /** - * @expectedException UnexpectedValueException - * @expectedExceptionMessageRegExp /Worker class does not implement/ - */ public function testFromArrayWorkerInvalidInterface() { - $arrayJob = ['title' => 'PP\Lib\PersistentQueue\Job']; - Job::fromArray($arrayJob); + $this->expectExceptionMessageMatches("/Worker class does not implement/"); + $this->expectException(UnexpectedValueException::class); + $arrayJob = ['title' => \PP\Lib\PersistentQueue\Job::class]; + $job = Job::fromArray($arrayJob); + $job->getWorker(); } public function testFromArrayWorker() { - $worker = $this->getMockForAbstractClass('PP\Lib\PersistentQueue\WorkerInterface', []); - $workerClass = get_class($worker); + $worker = $this->getMockForAbstractClass(\PP\Lib\PersistentQueue\WorkerInterface::class, []); + $workerClass = $worker::class; $arrayJob = ['title' => $workerClass]; $job = Job::fromArray($arrayJob); @@ -51,7 +48,7 @@ public function testFromArrayWorker() { public function testToArray() { $job = new Job(); - $worker = $this->getMockForAbstractClass('PP\Lib\PersistentQueue\WorkerInterface', []); + $worker = $this->getMockForAbstractClass(\PP\Lib\PersistentQueue\WorkerInterface::class, []); $job->setPayload(['test_key' => 'test_value']); $job->setWorker($worker); $job->setOwnerId(2); @@ -59,7 +56,7 @@ public function testToArray() { $expected = [ 'id' => 0, - 'title' => get_class($worker), + 'title' => $worker::class, 'payload' => ['test_key' => 'test_value'], 'state' => Job::STATE_FRESH, 'sys_owner' => 2, diff --git a/tests/Unit/PP/Lib/PersistentQueue/QueueTest.php b/tests/Unit/PP/Lib/PersistentQueue/QueueTest.php index db4098e0..2c769d67 100644 --- a/tests/Unit/PP/Lib/PersistentQueue/QueueTest.php +++ b/tests/Unit/PP/Lib/PersistentQueue/QueueTest.php @@ -2,43 +2,36 @@ namespace Tests\Unit\PP\Lib\PersistentQueue; -use Tests\Base\AbstractUnitTest; +use PHPUnit\Framework\MockObject\MockObject; +use PP\Lib\Config\ApplicationInterface; use PP\Lib\PersistentQueue\Job; use PP\Lib\PersistentQueue\Queue; +use Tests\Base\AbstractUnitTest; class QueueTest extends AbstractUnitTest { - /** - * @var Queue - */ - protected $queue; + protected Queue $queue; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $db; + protected \PXDatabase|MockObject $db; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $app; + protected ApplicationInterface|MockObject $app; /** * @before */ public function before() { - $this->app = $this->getMockBuilder('PXApplication') + $this->app = $this->getMockBuilder(\PXApplication::class) ->disableOriginalConstructor() - ->setMethods(['initContentObject']) + ->onlyMethods(['initContentObject']) ->getMock(); $this->app->types = [ - Queue::JOB_DB_TYPE => Queue::JOB_DB_TYPE + Queue::JOB_DB_TYPE => new \PXTypeDescription() ]; - $this->db = $this->getMockBuilder('PXDatabase') + $this->db = $this->getMockBuilder(\PXDatabase::class) ->disableOriginalConstructor() - ->setMethods(['addContentObject', 'modifyContentObject', 'getObjectsByFieldLimited']) + ->onlyMethods(['addContentObject', 'modifyContentObject', 'getObjectsByFieldLimited']) ->getMock(); $this->queue = new Queue($this->app, $this->db); @@ -51,7 +44,7 @@ public function testStartJob() { $this->db->expects($this->once()) ->method('modifyContentObject') ->with( - $this->equalTo(Queue::JOB_DB_TYPE), + $this->equalTo($this->app->getDataType(Queue::JOB_DB_TYPE)), $this->equalTo($expected->toArray()) ); @@ -65,7 +58,7 @@ public function testFailJob() { $this->db->expects($this->once()) ->method('modifyContentObject') ->with( - $this->equalTo(Queue::JOB_DB_TYPE), + $this->equalTo($this->app->getDataType(Queue::JOB_DB_TYPE)), $this->equalTo($expected->toArray()) ); @@ -79,7 +72,7 @@ public function testFinishJob() { $this->db->expects($this->once()) ->method('modifyContentObject') ->with( - $this->equalTo(Queue::JOB_DB_TYPE), + $this->equalTo($this->app->getDataType(Queue::JOB_DB_TYPE)), $this->equalTo($expected->toArray()) ); @@ -91,17 +84,17 @@ public function testGetFreshJobs() { $jobOne = (new Job()) ->setPayload(['test_id' => 1]) ->setId(1) - ->setWorker($this->getMockForAbstractClass('PP\Lib\PersistentQueue\WorkerInterface', [])); + ->setWorker($this->getMockForAbstractClass(\PP\Lib\PersistentQueue\WorkerInterface::class, [])); $jobTwo = (new Job()) ->setPayload(['test_id' => 2]) ->setId(2) - ->setWorker($this->getMockForAbstractClass('PP\Lib\PersistentQueue\WorkerInterface', [])); + ->setWorker($this->getMockForAbstractClass(\PP\Lib\PersistentQueue\WorkerInterface::class, [])); $this->db->expects($this->once()) ->method('getObjectsByFieldLimited') ->with( - $this->equalTo(Queue::JOB_DB_TYPE), + $this->equalTo($this->app->getDataType(Queue::JOB_DB_TYPE)), $this->equalTo(true), $this->equalTo('state'), $this->equalTo(Job::STATE_FRESH), @@ -117,7 +110,7 @@ public function testGetFreshJobs() { public function testAddJob() { $job = (new Job()) - ->setWorker($this->getMockForAbstractClass('PP\Lib\PersistentQueue\WorkerInterface', [])); + ->setWorker($this->getMockForAbstractClass(\PP\Lib\PersistentQueue\WorkerInterface::class, [])); $stub = ['stub_param' => 'stub_value']; @@ -129,7 +122,7 @@ public function testAddJob() { $this->db->expects($this->once()) ->method('addContentObject') ->with( - $this->equalTo(Queue::JOB_DB_TYPE), + $this->equalTo($this->app->getDataType(Queue::JOB_DB_TYPE)), array_merge($stub, $job->toArray()) ) ->willReturn(1); diff --git a/tests/Unit/PP/Lib/UrlGenerator/AdminUrlGeneratorTest.php b/tests/Unit/PP/Lib/UrlGenerator/AdminUrlGeneratorTest.php index 80ee8d23..d86bb325 100644 --- a/tests/Unit/PP/Lib/UrlGenerator/AdminUrlGeneratorTest.php +++ b/tests/Unit/PP/Lib/UrlGenerator/AdminUrlGeneratorTest.php @@ -2,6 +2,7 @@ namespace Tests\Unit\PP\Lib\UrlGenerator; +use PHPUnit\Framework\MockObject\MockObject; use PP\Lib\UrlGenerator\AdminUrlGenerator; use PP\Lib\UrlGenerator\ContextUrlGenerator; use Tests\Base\AbstractUnitTest; @@ -9,6 +10,24 @@ class AdminUrlGeneratorTest extends AbstractUnitTest { + protected AdminUrlGenerator|MockObject $generator; + + protected MockObject|ContextUrlGenerator $content; + + /** + * @before + */ + public function before() { + $this->content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') + ->addMethods(['_']) + ->getMock(); + + $this->generator = $this->getMockBuilder('PP\Lib\UrlGenerator\AdminUrlGenerator') + ->setConstructorArgs([$this->content]) + ->onlyMethods(['indexUrl', 'actionUrl', 'jsonUrl', 'popupUrl']) + ->getMock(); + } + public function testIndexUrlFromTargetModule() { $testArea = 'targetArea'; $params = [ @@ -17,9 +36,9 @@ public function testIndexUrlFromTargetModule() { ]; $expectedUrl = '/admin/?area=targetArea&a=1-%2B&b=2+2+%D0%B0'; - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ + /** @var MockObject | ContextUrlGenerator $content */ $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') - ->setMethods(['_']) + ->addMethods(['_']) ->getMock(); $content->setTargetModule($testArea); @@ -38,9 +57,9 @@ public function testIndexUrlFromCurrentModule() { ]; $expectedUrl = '/admin/?area=currentArea&a=1&b=2'; - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ + /** @var MockObject | ContextUrlGenerator $content */ $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') - ->setMethods(['_']) + ->addMethods(['_']) ->getMock(); $content->setCurrentModule($testArea); @@ -51,14 +70,13 @@ public function testIndexUrlFromCurrentModule() { $this->assertEquals($expectedUrl, $actualUrl); } - /** - * @expectedException \Exception - * @expectedExceptionMessage Don't given target module and current module. - */ public function testIndexUrlError() { - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ + $this->expectExceptionMessage("Don't given target module and current module."); + $this->expectException(\Exception::class); + + /** @var MockObject | ContextUrlGenerator $content */ $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') - ->setMethods(['_']) + ->addMethods(['_']) ->getMock(); $generator = new AdminUrlGenerator($content); $actualUrl = $generator->indexUrl([]); @@ -68,14 +86,14 @@ public function testIndexUrlError() { * @dataProvider actionUrlProvider */ public function testActionUrl($sid, $testArea, $overriderParams, $expectedActionUrl) { - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ + /** @var MockObject | ContextUrlGenerator $content */ $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') - ->setMethods(['_']) + ->addMethods(['_']) ->getMock(); - /** @var \PHPUnit_Framework_MockObject_MockObject | \PXRequest $request */ + /** @var MockObject | \PXRequest $request */ $request = $this->getMockBuilder('PXRequest') ->disableOriginalConstructor() - ->setMethods(['getSid']) + ->onlyMethods(['getSid']) ->getMock(); $request->expects($this->any()) @@ -116,9 +134,9 @@ public function testJsonUrl() { ]; $expectedUrl = '/admin/json.phtml?area=targetArea&a=1&b=2'; - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ + /** @var MockObject | ContextUrlGenerator $content */ $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') - ->setMethods(['_']) + ->addMethods(['_']) ->getMock(); $content->setTargetModule($testArea); @@ -133,14 +151,14 @@ public function testJsonUrl() { * @dataProvider popupUrlProvider */ public function testPopupUrl($sid, $testArea, $overriderParams, $expectedPopupUrl) { - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ + /** @var MockObject | ContextUrlGenerator $content */ $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') - ->setMethods(['_']) + ->addMethods(['_']) ->getMock(); - /** @var \PHPUnit_Framework_MockObject_MockObject | \PXRequest $request */ + /** @var MockObject | \PXRequest $request */ $request = $this->getMockBuilder('PXRequest') ->disableOriginalConstructor() - ->setMethods(['getSid']) + ->onlyMethods(['getSid']) ->getMock(); $request->expects($this->any()) @@ -173,53 +191,50 @@ public function popupUrlProvider() { ]; } - public function testGenerate() { - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ - $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') - ->setMethods(['_']) - ->getMock(); - - /** @var AdminUrlGenerator | \PHPUnit_Framework_MockObject_MockObject $generator */ - $generator = $this->getMockBuilder('PP\Lib\UrlGenerator\AdminUrlGenerator') - ->setConstructorArgs([$content]) - ->setMethods(['indexUrl', 'actionUrl', 'jsonUrl', 'popupUrl']) - ->getMock(); - - $generator->expects($this->at(0)) + public function testGenerateActionIndex() { + $this->generator->expects($this->once()) ->method('indexUrl') ->with([]); - $generator->expects($this->at(1)) + + $this->content->setTargetAction(ModuleInterface::ACTION_INDEX); + $this->generator->generate([]); + } + + public function testGenerateActionAction() { + $this->generator->expects($this->once()) ->method('actionUrl') ->with([]); - $generator->expects($this->at(2)) + + $this->content->setTargetAction(ModuleInterface::ACTION_ACTION); + $this->generator->generate([]); + } + + public function testGenerateActionJson() { + $this->generator->expects($this->once()) ->method('jsonUrl') ->with([]); - $generator->expects($this->at(3)) - ->method('popupUrl') - ->with([]); - - $content->setTargetAction(ModuleInterface::ACTION_INDEX); - $generator->generate([]); - $content->setTargetAction(ModuleInterface::ACTION_ACTION); - $generator->generate([]); + $this->content->setTargetAction(ModuleInterface::ACTION_JSON); + $this->generator->generate([]); + } - $content->setTargetAction(ModuleInterface::ACTION_JSON); - $generator->generate([]); + public function testGenerateActionPopup() { + $this->generator->expects($this->once()) + ->method('popupUrl') + ->with([]); - $content->setTargetAction(ModuleInterface::ACTION_POPUP); - $generator->generate([]); + $this->content->setTargetAction(ModuleInterface::ACTION_POPUP); + $this->generator->generate([]); } - /** - * @expectedException \Exception - * @expectedExceptionMessage Action 'targetAction' doesn't exist. - */ public function testGenerateError() { + $this->expectExceptionMessage("Action 'targetAction' doesn't exist."); + $this->expectException(\Exception::class); + $targetAction = 'targetAction'; - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ + /** @var MockObject | ContextUrlGenerator $content */ $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') - ->setMethods(['_']) + ->addMethods(['_']) ->getMock(); $content->setTargetAction($targetAction); @@ -227,7 +242,7 @@ public function testGenerateError() { /** @var AdminUrlGenerator $generator */ $generator = $this->getMockBuilder('PP\Lib\UrlGenerator\AdminUrlGenerator') ->setConstructorArgs([$content]) - ->setMethods(['indexUrl', 'actionUrl', 'jsonUrl', 'popupUrl']) + ->onlyMethods(['indexUrl', 'actionUrl', 'jsonUrl', 'popupUrl']) ->getMock(); $generator->generate([]); diff --git a/tests/Unit/PP/Lib/UrlGenerator/UrlGeneratorTest.php b/tests/Unit/PP/Lib/UrlGenerator/UrlGeneratorTest.php index 20ded9a9..366d8c33 100644 --- a/tests/Unit/PP/Lib/UrlGenerator/UrlGeneratorTest.php +++ b/tests/Unit/PP/Lib/UrlGenerator/UrlGeneratorTest.php @@ -2,6 +2,7 @@ namespace Tests\Unit\PP\Lib\UrlGenerator; +use PHPUnit\Framework\MockObject\MockObject; use Tests\Base\AbstractUnitTest; use PP\Lib\UrlGenerator\UrlGenerator; use PP\Lib\UrlGenerator\ContextUrlGenerator; @@ -9,18 +10,18 @@ class UrlGeneratorTest extends AbstractUnitTest { public function testGetUserGeneratorForDifferentContext() { - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ - $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') + /** @var MockObject | ContextUrlGenerator $content */ + $content = $this->getMockBuilder(\PP\Lib\UrlGenerator\ContextUrlGenerator::class) ->getMock(); $urlGenerator = new UrlGenerator($content); $userUrlGenerator = $urlGenerator->getUserGenerator(); - $this->assertInstanceOf('PP\Lib\UrlGenerator\UserUrlGenerator', $userUrlGenerator); + $this->assertInstanceOf(\PP\Lib\UrlGenerator\UserUrlGenerator::class, $userUrlGenerator); $userUrlGenerator2 = $urlGenerator->getUserGenerator(); $this->assertSame($userUrlGenerator, $userUrlGenerator2); - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content2 */ - $content2 = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') + /** @var MockObject | ContextUrlGenerator $content2 */ + $content2 = $this->getMockBuilder(\PP\Lib\UrlGenerator\ContextUrlGenerator::class) ->getMock(); $urlGenerator->setContext($content2); $userUrlGenerator3 = $urlGenerator->getUserGenerator(); @@ -28,18 +29,18 @@ public function testGetUserGeneratorForDifferentContext() { } public function testGetAdminGeneratorForDifferentContext() { - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ - $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') + /** @var MockObject | ContextUrlGenerator $content */ + $content = $this->getMockBuilder(\PP\Lib\UrlGenerator\ContextUrlGenerator::class) ->getMock(); $urlGenerator = new UrlGenerator($content); $adminUrlGenerator = $urlGenerator->getAdminGenerator(); - $this->assertInstanceOf('PP\Lib\UrlGenerator\AdminUrlGenerator', $adminUrlGenerator); + $this->assertInstanceOf(\PP\Lib\UrlGenerator\AdminUrlGenerator::class, $adminUrlGenerator); $adminUrlGenerator2 = $urlGenerator->getAdminGenerator(); $this->assertSame($adminUrlGenerator, $adminUrlGenerator2); - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content2 */ - $content2 = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') + /** @var MockObject | ContextUrlGenerator $content2 */ + $content2 = $this->getMockBuilder(\PP\Lib\UrlGenerator\ContextUrlGenerator::class) ->getMock(); $urlGenerator->setContext($content2); $adminUrlGenerator3 = $urlGenerator->getAdminGenerator(); diff --git a/tests/Unit/PP/Lib/UrlGenerator/UserUrlGeneratorTest.php b/tests/Unit/PP/Lib/UrlGenerator/UserUrlGeneratorTest.php index 98482868..c91848e9 100644 --- a/tests/Unit/PP/Lib/UrlGenerator/UserUrlGeneratorTest.php +++ b/tests/Unit/PP/Lib/UrlGenerator/UserUrlGeneratorTest.php @@ -2,6 +2,8 @@ namespace Tests\Unit\PP\Lib\UrlGenerator; +use PHPUnit\Framework\MockObject\MockObject; +use PP\Lib\UrlGenerator\AdminUrlGenerator; use PP\Lib\UrlGenerator\UserUrlGenerator; use PP\Lib\UrlGenerator\ContextUrlGenerator; use Tests\Base\AbstractUnitTest; @@ -9,11 +11,28 @@ class UserUrlGeneratorTest extends AbstractUnitTest { + protected AdminUrlGenerator|MockObject $generator; + + protected MockObject|ContextUrlGenerator $content; + /** - * @expectedException \LogicException - * @expectedExceptionMessage You cannot use the method: + * @before */ + public function before() { + $this->content = $this->getMockBuilder(\PP\Lib\UrlGenerator\ContextUrlGenerator::class) + ->addMethods(['_']) + ->getMock(); + + $this->generator = $this->getMockBuilder(\PP\Lib\UrlGenerator\UserUrlGenerator::class) + ->setConstructorArgs([$this->content]) + ->onlyMethods(['indexUrl', 'actionUrl', 'jsonUrl', 'popupUrl']) + ->getMock(); + } + public function testIndexUrl() { + $this->expectExceptionMessage("You cannot use the method:"); + $this->expectException(\LogicException::class); + $generator = $this->getGenerator(); $generator->indexUrl([]); } @@ -41,11 +60,10 @@ public function testJsonUrl() { $this->assertEquals($expectedUrl, $actualUrl); } - /** - * @expectedException \LogicException - * @expectedExceptionMessage You cannot use the method: - */ public function testPopupUrl() { + $this->expectExceptionMessage("You cannot use the method:"); + $this->expectException(\LogicException::class); + $generator = $this->getGenerator(); $generator->popupUrl([]); } @@ -54,10 +72,10 @@ public function testPopupUrl() { * @return UserUrlGenerator */ protected function getGenerator() { - /** @var UserUrlGenerator | \PHPUnit_Framework_MockObject_MockObject $generator */ - $generator = $this->getMockBuilder('PP\Lib\UrlGenerator\UserUrlGenerator') + /** @var UserUrlGenerator|MockObject $generator */ + $generator = $this->getMockBuilder(\PP\Lib\UrlGenerator\UserUrlGenerator::class) ->disableOriginalConstructor() - ->setMethods(['getArea']) + ->onlyMethods(['getArea']) ->getMock(); $generator->expects($this->any()) @@ -67,61 +85,58 @@ protected function getGenerator() { return $generator; } - public function testGenerate() { - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ - $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') - ->setMethods(['_']) - ->getMock(); - - /** @var UserUrlGenerator | \PHPUnit_Framework_MockObject_MockObject $generator */ - $generator = $this->getMockBuilder('PP\Lib\UrlGenerator\UserUrlGenerator') - ->setConstructorArgs([$content]) - ->setMethods(['indexUrl', 'actionUrl', 'jsonUrl', 'popupUrl']) - ->getMock(); - - $generator->expects($this->at(0)) + public function testGenerateActionIndex() { + $this->generator->expects($this->once()) ->method('indexUrl') ->with([]); - $generator->expects($this->at(1)) + + $this->content->setTargetAction(ModuleInterface::ACTION_INDEX); + $this->generator->generate([]); + } + + public function testGenerateActionAction() { + $this->generator->expects($this->once()) ->method('actionUrl') ->with([]); - $generator->expects($this->at(2)) + + $this->content->setTargetAction(ModuleInterface::ACTION_ACTION); + $this->generator->generate([]); + } + + public function testGenerateActionJson() { + $this->generator->expects($this->once()) ->method('jsonUrl') ->with([]); - $generator->expects($this->at(3)) - ->method('popupUrl') - ->with([]); - - $content->setTargetAction(ModuleInterface::ACTION_INDEX); - $generator->generate([]); - $content->setTargetAction(ModuleInterface::ACTION_ACTION); - $generator->generate([]); + $this->content->setTargetAction(ModuleInterface::ACTION_JSON); + $this->generator->generate([]); + } - $content->setTargetAction(ModuleInterface::ACTION_JSON); - $generator->generate([]); + public function testGenerateActionPopup() { + $this->generator->expects($this->once()) + ->method('popupUrl') + ->with([]); - $content->setTargetAction(ModuleInterface::ACTION_POPUP); - $generator->generate([]); + $this->content->setTargetAction(ModuleInterface::ACTION_POPUP); + $this->generator->generate([]); } - /** - * @expectedException \Exception - * @expectedExceptionMessage Action 'targetAction' doesn't exist. - */ public function testGenerateError() { + $this->expectExceptionMessage("Action 'targetAction' doesn't exist."); + $this->expectException(\Exception::class); + $targetAction = 'targetAction'; - /** @var \PHPUnit_Framework_MockObject_MockObject | ContextUrlGenerator $content */ - $content = $this->getMockBuilder('PP\Lib\UrlGenerator\ContextUrlGenerator') - ->setMethods(['_']) + /** @var MockObject | ContextUrlGenerator $content */ + $content = $this->getMockBuilder(\PP\Lib\UrlGenerator\ContextUrlGenerator::class) + ->addMethods(['_']) ->getMock(); $content->setTargetAction($targetAction); /** @var UserUrlGenerator $generator */ - $generator = $this->getMockBuilder('PP\Lib\UrlGenerator\UserUrlGenerator') + $generator = $this->getMockBuilder(\PP\Lib\UrlGenerator\UserUrlGenerator::class) ->setConstructorArgs([$content]) - ->setMethods(['indexUrl', 'actionUrl', 'jsonUrl', 'popupUrl']) + ->onlyMethods(['indexUrl', 'actionUrl', 'jsonUrl', 'popupUrl']) ->getMock(); $generator->generate([]); diff --git a/tests/Unit/PP/Properties/EnvLoaderTest.php b/tests/Unit/PP/Properties/EnvLoaderTest.php index 8ea62a76..8ec47270 100644 --- a/tests/Unit/PP/Properties/EnvLoaderTest.php +++ b/tests/Unit/PP/Properties/EnvLoaderTest.php @@ -69,11 +69,10 @@ public function testArrayCheck() { } - /** - * @expectedException \PP\Properties\EnvLoaderException - * @expectedExceptionMessage EMPTY should not be empty - */ public function testEmptyNonEmptyFail() { + $this->expectExceptionMessage("EMPTY should not be empty"); + $this->expectException(\PP\Properties\EnvLoaderException::class); + (new EnvLoader(__DIR__, 'env.txt')) ->addRequired(['EMPTY', 'NOT_EMPTY']) ->load(); diff --git a/vendor/Smarty/Smarty_Compiler.class.php b/vendor/Smarty/Smarty_Compiler.class.php index e38f8936..55ef90ba 100644 --- a/vendor/Smarty/Smarty_Compiler.class.php +++ b/vendor/Smarty/Smarty_Compiler.class.php @@ -557,7 +557,7 @@ public function _compile_tag($template_tag) case 'php': /* handle folded tags replaced by {php} */ - [, $block] = each($this->_folded_blocks); + $block = current($this->_folded_blocks); $this->_current_line_no += substr_count($block[0], "\n"); /* the number of matched elements in the regexp in _compile_file() determins the type of folded tag that was found */