From aa5cdc1ab638847d988cb345d64d9719dc954f1a Mon Sep 17 00:00:00 2001 From: OStefan2001 Date: Wed, 22 Jul 2026 15:08:34 +0300 Subject: [PATCH 1/4] Generated the rest of .md files and build llms-full with updated content. New layout to 404 twig --- .../listen-for-android-install-referrer.md | 7 +- ...ers-in-the-same-app-for-the-same-action.md | 3 +- ...vider-bootstrap-modern-php-applications.md | 12 +- ...ifecycle-for-a-mezzio-based-application.md | 34 +- .../architecture/understanding-middleware.md | 33 +- .../best-practice/aptana-set-svn-keywords.md | 9 +- ...security-in-dotkernel-headless-platform.md | 52 +- ...golden-rules-of-professional-php-coding.md | 9 +- .../htaccess-301-redirect-non-www-to-www.md | 0 ...t-update-delete-statements-with-zend-db.md | 9 +- .../sql-queries-using-zend-db-select.md | 6 +- .../best-practice/subqueries-with-zend-db.md | 0 .../svn-export-in-a-virtual-host.md | 12 +- ...n-keywords-setup-in-php-ide-zend-studio.md | 3 +- .../using-like-wildcards-with-zend-db.md | 10 +- ...urning-the-fetch-functions-from-zend-db.md | 186 + ...estamp-on-a-field-that-record-date-time.md | 67 + ...inas-mvc-is-retiring-consider-it-solved.md | 94 + ...-client-migration-from-postman-to-bruno.md | 33 +- .../api-endpoint-to-collect-client-errors.md | 3 +- ...ntent-negotiation-in-dotkernel-rest-api.md | 31 +- .../dotkernel-api-1-0-0-released.md | 0 ...dotkernel-api-client-side-authorization.md | 0 ...dotkernel-api-server-side-authorization.md | 104 + .../dotkernel-api-versus-laminas-api-tools.md | 65 + ...ror-reporting-endpoint-in-dotkernel-api.md | 160 + ...to-implement-mailchimp-in-dotkernel-api.md | 103 + ...openapi-implementation-in-dotkernel-api.md | 141 + ...-cors-implementation-to-zend-expressive.md | 21 +- ...g-layer-to-wurfl-in-dotkernel-using-apc.md | 11 +- ...poser-support-in-your-dotkernel-project.md | 15 +- ...browser-detection-in-dotkernel-projects.md | 3 +- ...n-using-cookie-remember-me-in-dotkernel.md | 3 +- ...through-bootstrap-of-non-existent-files.md | 3 +- ...ching-in-dotkernel-using-zend-framework.md | 16 +- ...melcase-table-names-in-mysql-on-windows.md | 0 ...end-certified-engineers-zce-in-our-team.md | 4 +- .../configuring-the-cache-in-dotkernel.md | 71 + ...ade-easy-in-laminas-mezzio-applications.md | 178 + ...cting-mobile-devices-in-dotkernel-1-6-0.md | 116 + ...able-wurfl-redirect-for-mobile-browsers.md | 42 + ...ambiguation-dotkernel-1-and-dotkernel-3.md | 72 + .../doctrine-cache-using-symfony-cache.md | 144 + ...ctrine-enum-implementation-in-dotkernel.md | 276 + ...ts-and-services-north-american-relaunch.md | 43 + .../dotkernel/dotkernel-1-2-0-release.md | 63 + .../dotkernel/dotkernel-1-2-2-release.md | 6 +- .../dotkernel/dotkernel-1-3-0-release.md | 9 +- .../dotkernel/dotkernel-1-3-2-release.md | 3 +- .../dotkernel/dotkernel-1-5-0-released.md | 19 +- .../dotkernel/dotkernel-1-8-0-lts-released.md | 7 +- ...ernel-1-8-1-upgrade-from-1-8-0-released.md | 3 +- .../dotkernel/dotkernel-coding-standard.md | 0 ...l-database-naming-conventions-for-mysql.md | 6 +- ...o-microframework-and-laminas-components.md | 15 +- ...-best-choice-for-your-presentation-site.md | 15 +- .../dotkernel/dotkernel-on-nginx.md | 3 +- ...nel-reserved-variable-names-for-caching.md | 0 .../dotkernel/dotkernel-template-engine.md | 4 +- .../dotkernel-version-1-0-in-action.md | 13 +- ...-connections-and-character-set-in-mysql.md | 3 +- .../geoip-city-removed-from-dotkernel.md | 16 +- .../geoip-ip-address-location-in-dotkernel.md | 13 +- ...rrors-with-dot-errorhandler-and-dot-log.md | 19 +- ...ghcharts-integration-in-dotkernel-1-6-0.md | 80 + ...o-group-log-files-by-date-using-dot-log.md | 142 + ...ase-with-zend-framework-zend-db-adapter.md | 37 + .../how-to-use-alerts-in-dotkernel.md | 121 + ...d-hashing-api-from-php-5-5-in-dotkernel.md | 59 + ...extension-in-zend-server-5-6-on-windows.md | 88 + ...p-extension-in-zend-server-6-on-windows.md | 47 + ...ot-log-in-zend-expressive-and-dotkernel.md | 153 + ...-upgrade-of-wurfl-xml-file-in-dotkernel.md | 43 + ...ration-of-zend-framework-1-pear-channel.md | 3 +- .../new-features-in-zend-framework-1-12.md | 9 +- ...tter-and-templates-for-zend-studio-10-1.md | 3 +- ...ing-admin-folder-with-htaccess-in-plesk.md | 0 ...as-mail-with-symfony-mailer-in-dot-mail.md | 13 +- ...urfl-cloud-php-library-to-dotkernel-1-6.md | 3 +- ...sing-dot-email-component-and-zend-email.md | 7 +- .../dotkernel/templating-in-dotkernel3.md | 6 +- ...ng-dotkernel-with-composer-dependencies.md | 10 +- .../using-utf8-charset-in-dotkernel.md | 6 +- ...fl-cloud-integration-in-dotkernel-1-6-0.md | 6 +- ...api-license-incompatible-with-dotkernel.md | 20 +- ...nd-framework-integration-into-dotkernel.md | 6 +- ...th-and-zend-acl-integrated-in-dotkernel.md | 16 +- ...end-console-implementation-in-dotkernel.md | 3 +- ...rk-dropped-integration-of-wurfl-adapter.md | 4 +- .../zend-registry-usage-in-dotkernel.md | 0 ...dotkernel-refactor-of-dot-session-class.md | 26 +- ...tter-file-for-dotkernel-coding-standard.md | 3 +- .../development-report-december-11-2017.md | 46 + .../dotkernel3/dotkernel-admin-v4.md | 241 + .../dotkernel-admin-version-3-launched.md | 46 + ...tkernel-api-architecture-and-components.md | 132 + .../dotkernel-frontend-version-3-launched.md | 44 + .../dotkernel3-stable-release-version-1-0.md | 103 + .../php-8-3-support-in-dotkernel-admin.md | 99 + .../php-8-3-support-in-dotkernel-api.md | 15 +- .../php-8-3-support-in-dotkernel-frontend.md | 6 +- ...ry-admin-in-dotkernel-headless-platform.md | 30 +- ...the-root-of-dotkernel-headless-platform.md | 27 +- ...adless-platform-the-whats-hows-and-whys.md | 44 +- ...xecution-in-dotkernel-headless-platform.md | 49 +- ...maker-generate-common-code-in-dotkernel.md | 26 +- ...evolution-pattern-versus-api-versioning.md | 43 +- ...sed-one-time-password-totp-in-dotkernel.md | 19 +- ...ubmodule-in-dotkernel-headless-platform.md | 134 + ...adds-postgresql-native-uuid-and-php-8-5.md | 71 + ...cy-setup-in-dotkernel-using-mezzio-cors.md | 116 + ...reating-admin-accounts-in-dotkernel-api.md | 127 + ...database-migrations-and-how-to-use-them.md | 90 + .../doctrine-cache-in-mezzio-and-dotkernel.md | 185 + ...igration-without-dropping-custom-tables.md | 96 + ...ly-url-in-an-generic-laminas-mezzio-app.md | 97 + ...in-wsl2-php-mariadb-composer-phpmyadmin.md | 112 + ...ndpoints-in-dotkernel-api-using-dot-cli.md | 129 + ...-zend-expressive-2-to-zend-expressive-3.md | 174 + ...nsole-with-dot-cli-based-on-laminas-cli.md | 112 + ...an-for-documentation-in-dotkernel-api-3.md | 182 + ...sing-the-urlgenerator-work-in-fastroute.md | 79 + .../what-is-cross-origin-token-redemption.md | 59 + .../how-to/what-is-psr-7-and-how-to-use-it.md | 222 + public/{readme => llms-content}/index.md | 6 +- ...free-php-html-css-javascript-editor-ide.md | 59 + .../javascript/intro-to-jquery.md | 119 + .../javascript/javascript-email-validator.md | 0 ...-versus-lgpl-in-practice-dotkernel-case.md | 31 +- ...provements-psr-15-handlers-vite-phpstan.md | 18 +- ...ic-routing-using-fastroute-in-dotkernel.md | 12 +- ...5-compliant-handlers-in-dotkernel-light.md | 19 +- ...-php-apache-mariadb-composer-phpmyadmin.md | 138 + .../aptana-php-installation-in-aptana-2-x.md | 47 + ...er-unicode-support-in-mysql-5-5-utf8mb4.md | 67 + ...seeding-doctrine-data-fixtures-vs-phinx.md | 297 + .../end-of-support-for-php-5-2-x-branch.md | 11 +- ...oint-arithmetic-why-is-int-0-7-0-1-10-7.md | 24 +- .../how-to-upgrade-wamp-to-php-5-3-4.md | 15 +- .../mezzio-app-development-in-wsl2.md | 6 +- ...ased-no-upgrade-possible-for-wampserver.md | 6 +- ...ironment-development-staging-production.md | 16 +- .../php-support-back-in-aptana-3-0.md | 12 +- ...ion-using-pdo-and-zend-framework-part-2.md | 9 +- ...-injection-using-pdo-and-zend-framework.md | 91 + ...-to-mysql-server-on-plesk-based-servers.md | 60 + ...c-analysis-replacing-psalm-with-phpstan.md | 182 + ...-to-connect-to-dotkernel-tracker-mantis.md | 57 + .../using-php-7-express-in-zend-studio-13.md | 75 + ...-control-ignore-patterns-in-zend-studio.md | 35 + ...end-certified-engineer-in-dotboost-team.md | 34 + .../zend-server-5-5-quick-setup-on-windows.md | 44 + ...x-installing-pear-packages-with-php-7-2.md | 87 + ...s-the-intl-php-extension-problem-solved.md | 100 + ...-quality-how-to-setup-phpcs-in-phpstorm.md | 60 + ...debug-bar-a-very-helpfull-zf-debug-tool.md | 30 + ...tting-pear-channel-for-zend-framework-1.md | 36 + .../wurfl-php-api-libraries-gpl-versions.md | 32 + ...ork-1-12-4-released-with-security-fixes.md | 41 + .../zend-framework-1-7-0-released.md | 28 + .../zend-framework-1-end-of-life.md | 38 + ...r-accessible-repository-on-plesk-server.md | 58 + ...ecurity-fixes-in-zend-framework-1-12-12.md | 51 + public/llms-full.txt | 15200 ++++++++++++++++ public/llms.txt | 260 +- ...-injection-using-pdo-and-zend-framework.md | 70 - src/App/templates/error/404.html.twig | 19 +- ...ith-symfony-mailer-in-dot-mail.jsonld.twig | 74 +- 168 files changed, 23515 insertions(+), 449 deletions(-) rename public/{readme => llms-content}/android/listen-for-android-install-referrer.md (65%) rename public/{readme => llms-content}/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md (92%) rename public/{readme => llms-content}/architecture/configprovider-bootstrap-modern-php-applications.md (87%) rename public/{readme => llms-content}/architecture/request-lifecycle-for-a-mezzio-based-application.md (68%) rename public/{readme => llms-content}/architecture/understanding-middleware.md (72%) rename public/{readme => llms-content}/best-practice/aptana-set-svn-keywords.md (88%) rename public/{readme => llms-content}/best-practice/basic-security-in-dotkernel-headless-platform.md (61%) rename public/{readme => llms-content}/best-practice/golden-rules-of-professional-php-coding.md (82%) rename public/{readme => llms-content}/best-practice/htaccess-301-redirect-non-www-to-www.md (100%) rename public/{readme => llms-content}/best-practice/insert-update-delete-statements-with-zend-db.md (87%) rename public/{readme => llms-content}/best-practice/sql-queries-using-zend-db-select.md (92%) rename public/{readme => llms-content}/best-practice/subqueries-with-zend-db.md (100%) rename public/{readme => llms-content}/best-practice/svn-export-in-a-virtual-host.md (85%) rename public/{readme => llms-content}/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md (97%) rename public/{readme => llms-content}/best-practice/using-like-wildcards-with-zend-db.md (89%) create mode 100644 public/llms-content/best-practice/what-are-returning-the-fetch-functions-from-zend-db.md create mode 100644 public/llms-content/best-practice/why-use-current-timestamp-on-a-field-that-record-date-time.md create mode 100644 public/llms-content/best-practice/zf-is-retired-laminas-mvc-is-retiring-consider-it-solved.md rename public/{readme => llms-content}/dotkernel-api/api-client-migration-from-postman-to-bruno.md (70%) rename public/{readme => llms-content}/dotkernel-api/api-endpoint-to-collect-client-errors.md (87%) rename public/{readme => llms-content}/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md (72%) rename public/{readme => llms-content}/dotkernel-api/dotkernel-api-1-0-0-released.md (100%) rename public/{readme => llms-content}/dotkernel-api/dotkernel-api-client-side-authorization.md (100%) create mode 100644 public/llms-content/dotkernel-api/dotkernel-api-server-side-authorization.md create mode 100644 public/llms-content/dotkernel-api/dotkernel-api-versus-laminas-api-tools.md create mode 100644 public/llms-content/dotkernel-api/error-reporting-endpoint-in-dotkernel-api.md create mode 100644 public/llms-content/dotkernel-api/how-to-implement-mailchimp-in-dotkernel-api.md create mode 100644 public/llms-content/dotkernel-api/openapi-implementation-in-dotkernel-api.md rename public/{readme => llms-content}/dotkernel/adding-a-cors-implementation-to-zend-expressive.md (80%) rename public/{readme => llms-content}/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md (82%) rename public/{readme => llms-content}/dotkernel/adding-composer-support-in-your-dotkernel-project.md (84%) rename public/{readme => llms-content}/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md (95%) rename public/{readme => llms-content}/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md (96%) rename public/{readme => llms-content}/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md (94%) rename public/{readme => llms-content}/dotkernel/caching-in-dotkernel-using-zend-framework.md (75%) rename public/{readme => llms-content}/dotkernel/camelcase-table-names-in-mysql-on-windows.md (100%) rename public/{readme => llms-content}/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md (86%) create mode 100644 public/llms-content/dotkernel/configuring-the-cache-in-dotkernel.md create mode 100644 public/llms-content/dotkernel/dependency-injection-made-easy-in-laminas-mezzio-applications.md create mode 100644 public/llms-content/dotkernel/detecting-mobile-devices-in-dotkernel-1-6-0.md create mode 100644 public/llms-content/dotkernel/disable-wurfl-redirect-for-mobile-browsers.md create mode 100644 public/llms-content/dotkernel/disambiguation-dotkernel-1-and-dotkernel-3.md create mode 100644 public/llms-content/dotkernel/doctrine-cache-using-symfony-cache.md create mode 100644 public/llms-content/dotkernel/doctrine-enum-implementation-in-dotkernel.md create mode 100644 public/llms-content/dotkernel/dotboost-technologies-products-and-services-north-american-relaunch.md create mode 100644 public/llms-content/dotkernel/dotkernel-1-2-0-release.md rename public/{readme => llms-content}/dotkernel/dotkernel-1-2-2-release.md (88%) rename public/{readme => llms-content}/dotkernel/dotkernel-1-3-0-release.md (87%) rename public/{readme => llms-content}/dotkernel/dotkernel-1-3-2-release.md (92%) rename public/{readme => llms-content}/dotkernel/dotkernel-1-5-0-released.md (71%) rename public/{readme => llms-content}/dotkernel/dotkernel-1-8-0-lts-released.md (93%) rename public/{readme => llms-content}/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md (95%) rename public/{readme => llms-content}/dotkernel/dotkernel-coding-standard.md (100%) rename public/{readme => llms-content}/dotkernel/dotkernel-database-naming-conventions-for-mysql.md (89%) rename public/{readme => llms-content}/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md (75%) rename public/{readme => llms-content}/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md (88%) rename public/{readme => llms-content}/dotkernel/dotkernel-on-nginx.md (96%) rename public/{readme => llms-content}/dotkernel/dotkernel-reserved-variable-names-for-caching.md (100%) rename public/{readme => llms-content}/dotkernel/dotkernel-template-engine.md (67%) rename public/{readme => llms-content}/dotkernel/dotkernel-version-1-0-in-action.md (81%) rename public/{readme => llms-content}/dotkernel/forcing-utf8-connections-and-character-set-in-mysql.md (96%) rename public/{readme => llms-content}/dotkernel/geoip-city-removed-from-dotkernel.md (82%) rename public/{readme => llms-content}/dotkernel/geoip-ip-address-location-in-dotkernel.md (84%) rename public/{readme => llms-content}/dotkernel/handling-and-logging-errors-with-dot-errorhandler-and-dot-log.md (80%) create mode 100644 public/llms-content/dotkernel/highcharts-integration-in-dotkernel-1-6-0.md create mode 100644 public/llms-content/dotkernel/how-to-group-log-files-by-date-using-dot-log.md create mode 100644 public/llms-content/dotkernel/how-to-set-a-persistent-connection-to-database-with-zend-framework-zend-db-adapter.md create mode 100644 public/llms-content/dotkernel/how-to-use-alerts-in-dotkernel.md create mode 100644 public/llms-content/dotkernel/implementing-the-new-password-hashing-api-from-php-5-5-in-dotkernel.md create mode 100644 public/llms-content/dotkernel/installing-geoip-extension-in-zend-server-5-6-on-windows.md create mode 100644 public/llms-content/dotkernel/installing-geoip-extension-in-zend-server-6-on-windows.md create mode 100644 public/llms-content/dotkernel/logging-with-dot-log-in-zend-expressive-and-dotkernel.md create mode 100644 public/llms-content/dotkernel/manual-upgrade-of-wurfl-xml-file-in-dotkernel.md rename public/{readme => llms-content}/dotkernel/migration-of-zend-framework-1-pear-channel.md (92%) rename public/{readme => llms-content}/dotkernel/new-features-in-zend-framework-1-12.md (88%) rename public/{readme => llms-content}/dotkernel/php-formatter-and-templates-for-zend-studio-10-1.md (92%) rename public/{readme => llms-content}/dotkernel/protecting-admin-folder-with-htaccess-in-plesk.md (100%) rename public/{readme => llms-content}/dotkernel/replacing-laminas-mail-with-symfony-mailer-in-dot-mail.md (82%) rename public/{readme => llms-content}/dotkernel/scientia-mobile-licensed-its-wurfl-cloud-php-library-to-dotkernel-1-6.md (95%) rename public/{readme => llms-content}/dotkernel/sending-emails-using-dot-email-component-and-zend-email.md (89%) rename public/{readme => llms-content}/dotkernel/templating-in-dotkernel3.md (90%) rename public/{readme => llms-content}/dotkernel/using-dotkernel-with-composer-dependencies.md (86%) rename public/{readme => llms-content}/dotkernel/using-utf8-charset-in-dotkernel.md (91%) rename public/{readme => llms-content}/dotkernel/wurfl-cloud-integration-in-dotkernel-1-6-0.md (90%) rename public/{readme => llms-content}/dotkernel/wurfl-php-api-license-incompatible-with-dotkernel.md (63%) rename public/{readme => llms-content}/dotkernel/wurfl-zend-framework-integration-into-dotkernel.md (95%) rename public/{readme => llms-content}/dotkernel/zend-auth-and-zend-acl-integrated-in-dotkernel.md (83%) rename public/{readme => llms-content}/dotkernel/zend-console-implementation-in-dotkernel.md (94%) rename public/{readme => llms-content}/dotkernel/zend-framework-dropped-integration-of-wurfl-adapter.md (85%) rename public/{readme => llms-content}/dotkernel/zend-registry-usage-in-dotkernel.md (100%) rename public/{readme => llms-content}/dotkernel/zend-session-usage-in-dotkernel-refactor-of-dot-session-class.md (63%) rename public/{readme => llms-content}/dotkernel/zend-studio-php-formatter-file-for-dotkernel-coding-standard.md (94%) create mode 100644 public/llms-content/dotkernel3/development-report-december-11-2017.md create mode 100644 public/llms-content/dotkernel3/dotkernel-admin-v4.md create mode 100644 public/llms-content/dotkernel3/dotkernel-admin-version-3-launched.md create mode 100644 public/llms-content/dotkernel3/dotkernel-api-architecture-and-components.md create mode 100644 public/llms-content/dotkernel3/dotkernel-frontend-version-3-launched.md create mode 100644 public/llms-content/dotkernel3/dotkernel3-stable-release-version-1-0.md create mode 100644 public/llms-content/dotkernel3/php-8-3-support-in-dotkernel-admin.md rename public/{readme => llms-content}/dotkernel3/php-8-3-support-in-dotkernel-api.md (83%) rename public/{readme => llms-content}/dotkernel3/php-8-3-support-in-dotkernel-frontend.md (89%) rename public/{readme => llms-content}/headless-platform/complementary-admin-in-dotkernel-headless-platform.md (71%) rename public/{readme => llms-content}/headless-platform/dotkernel-api-v6-the-root-of-dotkernel-headless-platform.md (75%) rename public/{readme => llms-content}/headless-platform/dotkernel-headless-platform-the-whats-hows-and-whys.md (67%) rename public/{readme => llms-content}/headless-platform/dotkernel-queue-asynchronous-execution-in-dotkernel-headless-platform.md (67%) rename public/{readme => llms-content}/headless-platform/dotmaker-generate-common-code-in-dotkernel.md (80%) rename public/{readme => llms-content}/headless-platform/evolution-pattern-versus-api-versioning.md (69%) rename public/{readme => llms-content}/headless-platform/implementing-time-based-one-time-password-totp-in-dotkernel.md (87%) create mode 100644 public/llms-content/headless-platform/shared-core-submodule-in-dotkernel-headless-platform.md create mode 100644 public/llms-content/headless-platform/version-7-adds-postgresql-native-uuid-and-php-8-5.md create mode 100644 public/llms-content/how-to/cors-policy-setup-in-dotkernel-using-mezzio-cors.md create mode 100644 public/llms-content/how-to/creating-admin-accounts-in-dotkernel-api.md create mode 100644 public/llms-content/how-to/database-migrations-and-how-to-use-them.md create mode 100644 public/llms-content/how-to/doctrine-cache-in-mezzio-and-dotkernel.md create mode 100644 public/llms-content/how-to/generating-a-doctrine-migration-without-dropping-custom-tables.md create mode 100644 public/llms-content/how-to/implementation-of-seo-friendly-url-in-an-generic-laminas-mezzio-app.md create mode 100644 public/llms-content/how-to/installing-almalinux-10-in-wsl2-php-mariadb-composer-phpmyadmin.md create mode 100644 public/llms-content/how-to/list-available-endpoints-in-dotkernel-api-using-dot-cli.md create mode 100644 public/llms-content/how-to/migrating-dotkernel-3-from-zend-expressive-2-to-zend-expressive-3.md create mode 100644 public/llms-content/how-to/replacing-dot-console-with-dot-cli-based-on-laminas-cli.md create mode 100644 public/llms-content/how-to/using-postman-for-documentation-in-dotkernel-api-3.md create mode 100644 public/llms-content/how-to/using-the-urlgenerator-work-in-fastroute.md create mode 100644 public/llms-content/how-to/what-is-cross-origin-token-redemption.md create mode 100644 public/llms-content/how-to/what-is-psr-7-and-how-to-use-it.md rename public/{readme => llms-content}/index.md (92%) create mode 100644 public/llms-content/javascript/codelobster-php-edition-free-php-html-css-javascript-editor-ide.md create mode 100644 public/llms-content/javascript/intro-to-jquery.md rename public/{readme => llms-content}/javascript/javascript-email-validator.md (100%) rename public/{readme => llms-content}/licensing/mit-versus-lgpl-in-practice-dotkernel-case.md (50%) rename public/{readme => llms-content}/middleware/dotkernel-light-improvements-psr-15-handlers-vite-phpstan.md (70%) rename public/{readme => llms-content}/middleware/handling-dynamic-routing-using-fastroute-in-dotkernel.md (90%) rename public/{readme => llms-content}/middleware/replacing-controllers-with-psr-15-compliant-handlers-in-dotkernel-light.md (86%) create mode 100644 public/llms-content/php-development/almalinux-9-in-wsl2-install-php-apache-mariadb-composer-phpmyadmin.md create mode 100644 public/llms-content/php-development/aptana-php-installation-in-aptana-2-x.md create mode 100644 public/llms-content/php-development/better-unicode-support-in-mysql-5-5-utf8mb4.md create mode 100644 public/llms-content/php-development/database-seeding-doctrine-data-fixtures-vs-phinx.md rename public/{readme => llms-content}/php-development/end-of-support-for-php-5-2-x-branch.md (72%) rename public/{readme => llms-content}/php-development/floating-point-arithmetic-why-is-int-0-7-0-1-10-7.md (69%) rename public/{readme => llms-content}/php-development/how-to-upgrade-wamp-to-php-5-3-4.md (78%) rename public/{readme => llms-content}/php-development/mezzio-app-development-in-wsl2.md (86%) rename public/{readme => llms-content}/php-development/php-5-3-6-released-no-upgrade-possible-for-wampserver.md (86%) rename public/{readme => llms-content}/php-development/php-environment-development-staging-production.md (69%) rename public/{readme => llms-content}/php-development/php-support-back-in-aptana-3-0.md (76%) rename public/{readme => llms-content}/php-development/protection-against-sql-injection-using-pdo-and-zend-framework-part-2.md (89%) create mode 100644 public/llms-content/php-development/protection-against-sql-injection-using-pdo-and-zend-framework.md create mode 100644 public/llms-content/php-development/remote-connections-to-mysql-server-on-plesk-based-servers.md create mode 100644 public/llms-content/php-development/static-analysis-replacing-psalm-with-phpstan.md create mode 100644 public/llms-content/php-development/using-aptana-to-connect-to-dotkernel-tracker-mantis.md create mode 100644 public/llms-content/php-development/using-php-7-express-in-zend-studio-13.md create mode 100644 public/llms-content/php-development/version-control-ignore-patterns-in-zend-studio.md create mode 100644 public/llms-content/php-development/welcome-to-the-10th-zend-certified-engineer-in-dotboost-team.md create mode 100644 public/llms-content/php-development/zend-server-5-5-quick-setup-on-windows.md create mode 100644 public/llms-content/php-troubleshooting/fix-installing-pear-packages-with-php-7-2.md create mode 100644 public/llms-content/php-troubleshooting/where-is-the-intl-php-extension-problem-solved.md create mode 100644 public/llms-content/phpstorm/code-quality-how-to-setup-phpcs-in-phpstorm.md create mode 100644 public/llms-content/zend-framework/scienta-zf-debug-bar-a-very-helpfull-zf-debug-tool.md create mode 100644 public/llms-content/zend-framework/sunsetting-pear-channel-for-zend-framework-1.md create mode 100644 public/llms-content/zend-framework/wurfl-php-api-libraries-gpl-versions.md create mode 100644 public/llms-content/zend-framework/zend-framework-1-12-4-released-with-security-fixes.md create mode 100644 public/llms-content/zend-framework/zend-framework-1-7-0-released.md create mode 100644 public/llms-content/zend-framework/zend-framework-1-end-of-life.md create mode 100644 public/llms-content/zend-framework/zend-framework-as-pear-accessible-repository-on-plesk-server.md create mode 100644 public/llms-content/zend-framework/zend-mail-and-zend-http-security-fixes-in-zend-framework-1-12-12.md create mode 100644 public/llms-full.txt delete mode 100644 public/readme/php-development/protection-against-sql-injection-using-pdo-and-zend-framework.md diff --git a/public/readme/android/listen-for-android-install-referrer.md b/public/llms-content/android/listen-for-android-install-referrer.md similarity index 65% rename from public/readme/android/listen-for-android-install-referrer.md rename to public/llms-content/android/listen-for-android-install-referrer.md index 46353699..1f72e30d 100644 --- a/public/readme/android/listen-for-android-install-referrer.md +++ b/public/llms-content/android/listen-for-android-install-referrer.md @@ -12,12 +12,15 @@ language: "en" ## Getting Referrer Data at Install Time -Android market sends information at the moment of app install, delivered as a broadcasted intent by Android market at install time - even before the app is opened for the first time. This can be used to create custom links to an Android application, including bits of information about the referrer, sent directly to the app for processing at install. It can be a simple and accurate solution for mobile app install tracking, among other uses. +Android market sends information at the moment of app install, delivered as a broadcasted intent by Android market at install time - even before the app is opened for the first time. +This can be used to create custom links to an Android application, including bits of information about the referrer, sent directly to the app for processing at install. +It can be a simple and accurate solution for mobile app install tracking, among other uses. ## FAQ **Q: Does Android send information when the app is installed?** -A: Yes. Android market broadcasts an intent containing referrer information at the moment the app is installed. +A: Yes. +Android market broadcasts an intent containing referrer information at the moment the app is installed. **Q: When is this referrer information available to the app?** A: It's delivered as a broadcasted intent at install time, before the app is ever opened. diff --git a/public/readme/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md b/public/llms-content/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md similarity index 92% rename from public/readme/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md rename to public/llms-content/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md index cb0eafe6..692bc596 100644 --- a/public/readme/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md +++ b/public/llms-content/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md @@ -12,7 +12,8 @@ language: "en" ## The problem -When multiple broadcast receivers are registered separately to listen for the same intent within the same Android app, this can lead to unexpected results: one broadcast receiver might consume the broadcasted intent, leaving the others with nothing to receive. This can happen when using 3rd party libraries that define their own broadcast receivers alongside an app's own receivers. +When multiple broadcast receivers are registered separately to listen for the same intent within the same Android app, this can lead to unexpected results: one broadcast receiver might consume the broadcasted intent, leaving the others with nothing to receive. +This can happen when using 3rd party libraries that define their own broadcast receivers alongside an app's own receivers. ## The approach diff --git a/public/readme/architecture/configprovider-bootstrap-modern-php-applications.md b/public/llms-content/architecture/configprovider-bootstrap-modern-php-applications.md similarity index 87% rename from public/readme/architecture/configprovider-bootstrap-modern-php-applications.md rename to public/llms-content/architecture/configprovider-bootstrap-modern-php-applications.md index a66607e6..2dc47fdf 100644 --- a/public/readme/architecture/configprovider-bootstrap-modern-php-applications.md +++ b/public/llms-content/architecture/configprovider-bootstrap-modern-php-applications.md @@ -12,11 +12,13 @@ language: "en" ## TL;DR -In PHP, a `ConfigProvider` is a class or callable that is part of an application's bootstrap process, returning configuration data that tells the platform which middleware should run, in what order, and under what conditions. Frameworks like Mezzio, Laminas, Slim, and the Dotkernel Headless Platform use ConfigProviders to declare middleware pipeline configuration, dependency injection mappings, and request handlers, which get merged together automatically during bootstrap (except in Dotkernel, where new ConfigProviders must be registered manually). +In PHP, a `ConfigProvider` is a class or callable that is part of an application's bootstrap process, returning configuration data that tells the platform which middleware should run, in what order, and under what conditions. +Frameworks like Mezzio, Laminas, Slim, and the Dotkernel Headless Platform use ConfigProviders to declare middleware pipeline configuration, dependency injection mappings, and request handlers, which get merged together automatically during bootstrap (except in Dotkernel, where new ConfigProviders must be registered manually). ## Where Is the ConfigProvider Used? -Mezzio (formerly Zend Expressive), Laminas, Slim, the Dotkernel Headless Platform, and other middleware-based frameworks often have a `ConfigProvider` class. In Laminas/Mezzio specifically, each module or package may contain a `ConfigProvider` that returns: +Mezzio (formerly Zend Expressive), Laminas, Slim, the Dotkernel Headless Platform, and other middleware-based frameworks often have a `ConfigProvider` class. +In Laminas/Mezzio specifically, each module or package may contain a `ConfigProvider` that returns: - Middleware pipeline configuration: - Middleware classes or service names. @@ -80,7 +82,8 @@ The ConfigProvider is automatically picked up by the framework during applicatio - **Modular** - Each package can ship with its own config without interfering with others. - **Container-friendly** - Works well with frameworks using DI containers like Laminas ServiceManager, PHP-DI, or Pimple. - **Standardized service definitions** - Consistent rules for object creation, separate from business logic. -- **Auto-Discovery** - In Laminas/Mezzio, the ConfigAggregator automatically loads and merges all ConfigProviders. Dotkernel is an exception: new ConfigProviders have to be added manually in `config/config.php`, because all the initial ConfigProviders required to install the applications are already injected. +- **Auto-Discovery** - In Laminas/Mezzio, the ConfigAggregator automatically loads and merges all ConfigProviders. +Dotkernel is an exception: new ConfigProviders have to be added manually in `config/config.php`, because all the initial ConfigProviders required to install the applications are already injected. - **Environment-agnostic** - Returns an array that defines dev, test, or prod environments. - **Testability** - The consistent, central configuration promotes isolated (e.g. per-module) testing, easier swapping of dependencies, and assertion of pipeline setup (e.g. checking if a config key is present). @@ -93,7 +96,8 @@ A: It is a class that is part of an application's bootstrap process: a class or A: In the Laminas/Mezzio ecosystem, it's literally an array of configuration, settings, or anything else the application needs, and each module or package may contain its own ConfigProvider returning middleware pipeline configuration, dependency injection mappings, and request handlers. **Q: What is the difference between 'factories' and 'invokables' in the dependencies array?** -A: `factories` will have the factory build the service, while `invokables` will use `new` directly. You can also use `aliases` to redirect to another service name and `delegators` to wrap the original service. +A: `factories` will have the factory build the service, while `invokables` will use `new` directly. +You can also use `aliases` to redirect to another service name and `delegators` to wrap the original service. **Q: How does the ConfigProvider get used during application bootstrap?** A: It is automatically picked up by the framework during bootstrap: all ConfigProviders are merged into one array, the configuration array is read, each item is resolved via `$app->pipe()`, the error-handling middleware is placed last in the pipeline, and at runtime Laminas Stratigility iterates over the pipeline in the order it was registered. diff --git a/public/readme/architecture/request-lifecycle-for-a-mezzio-based-application.md b/public/llms-content/architecture/request-lifecycle-for-a-mezzio-based-application.md similarity index 68% rename from public/readme/architecture/request-lifecycle-for-a-mezzio-based-application.md rename to public/llms-content/architecture/request-lifecycle-for-a-mezzio-based-application.md index 95f41e42..092d85f8 100644 --- a/public/readme/architecture/request-lifecycle-for-a-mezzio-based-application.md +++ b/public/llms-content/architecture/request-lifecycle-for-a-mezzio-based-application.md @@ -12,33 +12,44 @@ language: "en" ## TL;DR -The request lifecycle is the sequence of steps that happen from the moment a user makes an HTTP request until the server sends back a response. This is illustrated using Dotkernel Light, one of the applications in the Dotkernel Headless Platform suite, walking through entry point setup, routing, handler execution, template rendering, response creation, and the response emitter. +The request lifecycle is the sequence of steps that happen from the moment a user makes an HTTP request until the server sends back a response. +This is illustrated using Dotkernel Light, one of the applications in the Dotkernel Headless Platform suite, walking through entry point setup, routing, handler execution, template rendering, response creation, and the response emitter. ## The Request Lifecycle, Step by Step ### Entry Point 1. **HTTP Request** - Bootstrap the application, load configuration and create the Mezzio application instance. -2. **Service Container** - Register factories, aliases and delegators. All services are configured and ready to use. -3. **Route Registration** - Read all available routes with their allowed request methods and dynamically register them in the application. Routes are managed by FastRoute. Example: `/page/about` -> `GetPageViewHandler`, Method: `GET`, Route name: `page::about`. -4. **Middleware Pipeline** - Loads the predefined order of middleware. It defines how incoming HTTP requests move through the application and how responses are generated. +2. **Service Container** - Register factories, aliases and delegators. +All services are configured and ready to use. +3. **Route Registration** - Read all available routes with their allowed request methods and dynamically register them in the application. +Routes are managed by FastRoute. +Example: `/page/about` -> `GetPageViewHandler`, Method: `GET`, Route name: `page::about`. +4. **Middleware Pipeline** - Loads the predefined order of middleware. +It defines how incoming HTTP requests move through the application and how responses are generated. ### Processing -5. **Routing** - FastRoute matches the URL and method against registered routes. Match: `GET /page/about`, Handler: `GetPageViewHandler`, Route name: `page::about`. +5. **Routing** - FastRoute matches the URL and method against registered routes. +Match: `GET /page/about`, Handler: `GetPageViewHandler`, Route name: `page::about`. 6. **Handler Invocation** - Extract the matched route name from the request and pass it to the renderer: ```php $template = $request->getAttribute(RouteResult::class)->getMatchedRouteName(); // $template = 'page::about'; ``` -7. **Custom Logic Execution in Handler** - Execute the business logic in the handler. The process can involve services and any custom logic. -8. **Template Rendering** - Twig loads the template, applies the layout, renders blocks and includes partials. Load: `src/Page/templates/page/about.html.twig`, Extends: `@layout/default.html.twig`, Render blocks: `title`, `content`, Include partials: `alerts.html.twig`, etc., Output: Final HTML. -9. **Response Creation** - An `HtmlResponse` is created with status, headers and the rendered HTML body. Status: `200 OK`, Content-Type: `text/html; charset=utf-8`, Body: Rendered HTML. -10. **Response Pipeline** - The response flows back through the middleware stack. Middleware can modify headers, cookies, compress content, etc. +7. **Custom Logic Execution in Handler** - Execute the business logic in the handler. +The process can involve services and any custom logic. +8. **Template Rendering** - Twig loads the template, applies the layout, renders blocks and includes partials. +Load: `src/Page/templates/page/about.html.twig`, Extends: `@layout/default.html.twig`, Render blocks: `title`, `content`, Include partials: `alerts.html.twig`, etc., Output: Final HTML. +9. **Response Creation** - An `HtmlResponse` is created with status, headers and the rendered HTML body. +Status: `200 OK`, Content-Type: `text/html; charset=utf-8`, Body: Rendered HTML. +10. **Response Pipeline** - The response flows back through the middleware stack. +Middleware can modify headers, cookies, compress content, etc. ### Exit Point -11. **Response Emitter** - The final response is sent back to the browser. The page is rendered and sent to the user, as one of `HTTP 20x/30x`, `HTTP 40x`, or `HTTP 50x`. +11. **Response Emitter** - The final response is sent back to the browser. +The page is rendered and sent to the user, as one of `HTTP 20x/30x`, `HTTP 40x`, or `HTTP 50x`. ## FAQ @@ -58,7 +69,8 @@ A: The matched route name is extracted from the request attribute and passed to A: Twig loads the matched template file, applies the layout it extends, renders its blocks, and includes any partials, producing the final HTML output. **Q: How is the response created and returned to the browser?** -A: An `HtmlResponse` is created with a status code, headers, and the rendered HTML body. It then flows back through the middleware stack in reverse (the response pipeline), where middleware can modify headers, cookies, or compress content, before the response emitter sends the final response back to the browser as HTTP 20x/30x, 40x, or 50x. +A: An `HtmlResponse` is created with a status code, headers, and the rendered HTML body. +It then flows back through the middleware stack in reverse (the response pipeline), where middleware can modify headers, cookies, or compress content, before the response emitter sends the final response back to the browser as HTTP 20x/30x, 40x, or 50x. ## Resources diff --git a/public/readme/architecture/understanding-middleware.md b/public/llms-content/architecture/understanding-middleware.md similarity index 72% rename from public/readme/architecture/understanding-middleware.md rename to public/llms-content/architecture/understanding-middleware.md index 29a09a30..d2ccde1e 100644 --- a/public/readme/architecture/understanding-middleware.md +++ b/public/llms-content/architecture/understanding-middleware.md @@ -12,11 +12,13 @@ language: "en" ## TL;DR -Middleware is code that exists between the request and response: it can take an incoming request, act on it, and either complete the response itself or delegate to the next middleware in the queue. It's used for concerns like authentication, CORS, caching, rate limiting, and more, and in PHP a PSR-15 compliant middleware implements `Psr\Http\Server\MiddlewareInterface` with a single `process()` method. +Middleware is code that exists between the request and response: it can take an incoming request, act on it, and either complete the response itself or delegate to the next middleware in the queue. +It's used for concerns like authentication, CORS, caching, rate limiting, and more, and in PHP a PSR-15 compliant middleware implements `Psr\Http\Server\MiddlewareInterface` with a single `process()` method. ## The Purpose of Middleware -Middleware makes it easier for software developers to implement communication and input/output, so they can focus on the specific purpose of their application. In web services, the `Input` represents the `Request` received, and `Output` represents the `Response` to be sent. +Middleware makes it easier for software developers to implement communication and input/output, so they can focus on the specific purpose of their application. +In web services, the `Input` represents the `Request` received, and `Output` represents the `Response` to be sent. ## Using Middleware @@ -34,7 +36,8 @@ Middleware can be used for purposes such as, but not limited to: ## Usage -According to PSR-15: HTTP Server Request Handlers, a component that processes an incoming request and generates a response is a middleware. To be compliant with the PSR-15 standard, the middleware must implement `Psr\Http\Server\MiddlewareInterface`: +According to PSR-15: HTTP Server Request Handlers, a component that processes an incoming request and generates a response is a middleware. +To be compliant with the PSR-15 standard, the middleware must implement `Psr\Http\Server\MiddlewareInterface`: ```php class MyMiddleware implements MiddlewareInterface @@ -96,16 +99,22 @@ class ExampleMiddleware implements MiddlewareInterface ## How Middleware Is Called -The application pipeline defines the execution flow. The request passes through the middleware in the pipeline, one by one, in the order they are placed in the pipeline. Each middleware processes the request and/or response and either passes control to the next middleware in the chain or terminates the request and returns a response. +The application pipeline defines the execution flow. +The request passes through the middleware in the pipeline, one by one, in the order they are placed in the pipeline. +Each middleware processes the request and/or response and either passes control to the next middleware in the chain or terminates the request and returns a response. -- If control passes through all middleware successfully, execution is eventually passed to the custom code which generates a response of its own. Execution then passes through the middleware in reverse order and returns the response. +- If control passes through all middleware successfully, execution is eventually passed to the custom code which generates a response of its own. +Execution then passes through the middleware in reverse order and returns the response. - If execution is terminated before reaching the custom code (e.g. via an exception), the response is generated by the last middleware reached by the execution. ## Middleware in Practice -A simple real world example of middleware usage is the enhancement of a request with the user IP for logging purposes or building reports based on geographical data. For this example the pipeline has a single middleware. +A simple real world example of middleware usage is the enhancement of a request with the user IP for logging purposes or building reports based on geographical data. +For this example the pipeline has a single middleware. -The flow begins with a request. Execution passes control to the IP middleware, which enhances the request with the user's IP and other relevant data. Control passes to the custom handler that processes the request and returns a response. The flow continues in reverse order, back to the IP middleware, which can, if needed, change the output before it gets returned to the user that initiated the request. +The flow begins with a request. Execution passes control to the IP middleware, which enhances the request with the user's IP and other relevant data. +Control passes to the custom handler that processes the request and returns a response. +The flow continues in reverse order, back to the IP middleware, which can, if needed, change the output before it gets returned to the user that initiated the request. ## FAQ @@ -113,7 +122,8 @@ The flow begins with a request. Execution passes control to the IP middleware, w A: Middleware is code that exists between the request and response, and which can take the incoming request, perform actions based on it, and either complete the response or pass delegation on to the next middleware in the queue. **Q: What is the purpose of middleware?** -A: Middleware makes it easier for software developers to implement communication and input/output, so they can focus on the specific purpose of their application. In web services, the Input represents the Request received, and Output represents the Response to be sent. +A: Middleware makes it easier for software developers to implement communication and input/output, so they can focus on the specific purpose of their application. +In web services, the Input represents the Request received, and Output represents the Response to be sent. **Q: What can middleware be used for?** A: Middleware can be used for purposes such as A/B testing, debugging, caching, CORS, authentication (HTTP Basic Auth, OAuth 2.0, OpenID), CSRF protection, rate limiting, referrals, and IP restriction. @@ -122,10 +132,13 @@ A: Middleware can be used for purposes such as A/B testing, debugging, caching, A: According to PSR-15, a compliant middleware must implement `Psr\Http\Server\MiddlewareInterface`, which requires a `process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface` method. **Q: How does middleware get called within the application pipeline?** -A: The application pipeline defines the execution flow: the request passes through the middleware one by one, in the order they are placed. If control passes through all middleware successfully, execution is passed to your custom code, which generates a response, and execution then passes back through the middleware in reverse order. If execution is terminated before reaching your custom code (e.g. via an exception), the response is generated by the last middleware reached. +A: The application pipeline defines the execution flow: the request passes through the middleware one by one, in the order they are placed. If control passes through all middleware successfully, execution is passed to your custom code, which generates a response, and execution then passes back through the middleware in reverse order. +If execution is terminated before reaching your custom code (e.g. via an exception), the response is generated by the last middleware reached. **Q: What is a practical, real-world example of middleware?** -A: A simple example is enhancing a request with the user's IP for logging purposes or geographical reporting. The request first passes through the IP middleware, which enhances the request with the user's IP and other relevant data, then control passes to the custom handler that processes the request and returns a response. The flow continues in reverse, back through the IP middleware, which can change the output before it's returned to the user. +A: A simple example is enhancing a request with the user's IP for logging purposes or geographical reporting. +The request first passes through the IP middleware, which enhances the request with the user's IP and other relevant data, then control passes to the custom handler that processes the request and returns a response. +The flow continues in reverse, back through the IP middleware, which can change the output before it's returned to the user. ## Resources diff --git a/public/readme/best-practice/aptana-set-svn-keywords.md b/public/llms-content/best-practice/aptana-set-svn-keywords.md similarity index 88% rename from public/readme/best-practice/aptana-set-svn-keywords.md rename to public/llms-content/best-practice/aptana-set-svn-keywords.md index 7c13d5f0..0514236d 100644 --- a/public/readme/best-practice/aptana-set-svn-keywords.md +++ b/public/llms-content/best-practice/aptana-set-svn-keywords.md @@ -12,12 +12,14 @@ language: "en" ## Overview -In Aptana it's very simple to set the svn:keywords property for a file. For example, to set the svn keyword property `Id`: +In Aptana it's very simple to set the svn:keywords property for a file. +For example, to set the svn keyword property `Id`: ## Steps 1. In the file where the svn keyword property should be added, write `$Id$`. -2. Right click on the file, then follow Team -> Set Property... (Note: "Set Property..." will not be active if the file hasn't first been added to SVN via Team -> Add to Version Controller). +2. Right click on the file, then follow Team -> Set Property... +(Note: "Set Property..." will not be active if the file hasn't first been added to SVN via Team -> Add to Version Controller). 3. Select `svn:keywords`, and write `Id` in the text field. When the SVN commit of the file is made, the `$Id$` keyword will be replaced with text containing the file's SVN metadata, in a specific format. @@ -28,7 +30,8 @@ When the SVN commit of the file is made, the `$Id$` keyword will be replaced wit A: Write the keyword marker (for example `$Id$`) in the file, then right click the file and follow Team -> Set Property..., select `svn:keywords`, and write `Id` in the text field. **Q: Why is "Set Property..." not active when I right click the file?** -A: Set Property... will not be active if the file hasn't first been added to SVN. Use Team -> Add to Version Controller before trying to set the property. +A: Set Property... will not be active if the file hasn't first been added to SVN. +Use Team -> Add to Version Controller before trying to set the property. **Q: What happens to the $Id$ keyword after an SVN commit?** A: After the SVN commit of the file, the `$Id$` keyword is replaced with text containing the file's SVN metadata, in a specific format. diff --git a/public/readme/best-practice/basic-security-in-dotkernel-headless-platform.md b/public/llms-content/best-practice/basic-security-in-dotkernel-headless-platform.md similarity index 61% rename from public/readme/best-practice/basic-security-in-dotkernel-headless-platform.md rename to public/llms-content/best-practice/basic-security-in-dotkernel-headless-platform.md index 618b199d..5ee7435c 100644 --- a/public/readme/best-practice/basic-security-in-dotkernel-headless-platform.md +++ b/public/llms-content/best-practice/basic-security-in-dotkernel-headless-platform.md @@ -40,21 +40,27 @@ Dotkernel aims to: ## Form Input Validation -Never trust that user input is correct by passing it directly into business logic. By defining the configuration for an input filter, a field's presence and type are both ensured. Dotkernel API uses laminas/laminas-inputfilter for this purpose. Dotkernel Admin additionally uses laminas/laminas-form, which contains a thin layer of objects representing form elements, an InputFilter for each input (or custom validators), and methods for binding data to and from the form. laminas-form integrates with the Laminas Security Ecosystem: laminas-escaper, laminas-validator, laminas-session, and laminas-filter. +Never trust that user input is correct by passing it directly into business logic. +By defining the configuration for an input filter, a field's presence and type are both ensured. Dotkernel API uses laminas/laminas-inputfilter for this purpose. +Dotkernel Admin additionally uses laminas/laminas-form, which contains a thin layer of objects representing form elements, an InputFilter for each input (or custom validators), and methods for binding data to and from the form. laminas-form integrates with the Laminas Security Ecosystem: laminas-escaper, laminas-validator, laminas-session, and laminas-filter. ## Content Negotiation -Content negotiation is used in RESTful APIs so client and server agree on the format and language of exchanged data. Dotkernel API handles this via a middleware configured in `config/autoload/content-negotiation.global.php`, using the `Content-Type` and `Accept` HTTP request headers, and returning `application/json` or `application/hal+json` data formats. +Content negotiation is used in RESTful APIs so client and server agree on the format and language of exchanged data. +Dotkernel API handles this via a middleware configured in `config/autoload/content-negotiation.global.php`, using the `Content-Type` and `Accept` HTTP request headers, and returning `application/json` or `application/hal+json` data formats. ## Cross-Origin Resource Sharing -CORS is a browser security mechanism controlling how web pages can request resources from a different domain. In Dotkernel API, CORS is handled by mezzio/mezzio-cors and configured in `config/autoload/cors.local.php`. It starts detecting the proper `cors` configuration whenever it detects a `cors preflight`, validating the call using configuration items: origins, headers, max age, and credentials. +CORS is a browser security mechanism controlling how web pages can request resources from a different domain. +In Dotkernel API, CORS is handled by mezzio/mezzio-cors and configured in `config/autoload/cors.local.php`. +It starts detecting the proper `cors` configuration whenever it detects a `cors preflight`, validating the call using configuration items: origins, headers, max age, and credentials. > When configuring your pipeline, make sure to add the CorsMiddleware BEFORE the RouteMiddleware. ## Role-Based Access Control -RBAC manages access to resources by assigning roles to user types, which are in turn assigned to users requiring a certain level of access. Dotkernel API uses mezzio/mezzio-authorization-rbac for this purpose, with several predefined roles configurable in `config/autoload/authorization.global.php`. +RBAC manages access to resources by assigning roles to user types, which are in turn assigned to users requiring a certain level of access. +Dotkernel API uses mezzio/mezzio-authorization-rbac for this purpose, with several predefined roles configurable in `config/autoload/authorization.global.php`. ## Demo Credentials @@ -64,23 +70,29 @@ Demo credentials are provided in Dotkernel API for convenience, to allow easy te ## Error Reporting Endpoint and ErrorReportingTokens -The error reporting endpoint provides a reliable channel through which 3rd-party developers can report issues directly. Dotkernel API has a dedicated `/error-report` endpoint for this, using an `ErrorReportingToken` set up in `config/autoload/error-handling.global.php`. +The error reporting endpoint provides a reliable channel through which 3rd-party developers can report issues directly. +Dotkernel API has a dedicated `/error-report` endpoint for this, using an `ErrorReportingToken` set up in `config/autoload/error-handling.global.php`. ## OpenAPI Documentation -OpenAPI documentation (formerly Swagger) provides a standardized, machine-readable way to describe API requests and responses. It's critical for developer efficiency (streamlines front/back-end communication, allows mock servers before the backend is implemented), reliability (auto-generated docs, easier testing), and integration (tools like Postman and Codegen libraries). Dotkernel API implements zircote/swagger-php to provide interactive documentation. +OpenAPI documentation (formerly Swagger) provides a standardized, machine-readable way to describe API requests and responses. +It's critical for developer efficiency (streamlines front/back-end communication, allows mock servers before the backend is implemented), reliability (auto-generated docs, easier testing), and integration (tools like Postman and Codegen libraries). +Dotkernel API implements zircote/swagger-php to provide interactive documentation. -> Do not include sensitive information for your endpoints. Do not enable documentation in a production environment. +> Do not include sensitive information for your endpoints. +> Do not enable documentation in a production environment. ## PHP Dependencies -Modern PHP projects rely heavily on external packages via Composer, and there is a tangible risk of exposing an application through insecure dependencies. Dotkernel API has regular checks for vulnerable and outdated packages, including transient dependencies. +Modern PHP projects rely heavily on external packages via Composer, and there is a tangible risk of exposing an application through insecure dependencies. +Dotkernel API has regular checks for vulnerable and outdated packages, including transient dependencies. > Always use dependencies from reliable sources and keep them updated to their latest version. ## OAuth2 Security -OAuth 2.0 is a secure authorization framework letting one application access resources on behalf of a user without requiring the user's password, an industry standard for web, mobile, and API-based systems. Dotkernel API uses mezzio/mezzio-authentication-oauth2 for OAuth2 authentication. The package itself is secure, but it must be used properly: +OAuth 2.0 is a secure authorization framework letting one application access resources on behalf of a user without requiring the user's password, an industry standard for web, mobile, and API-based systems. Dotkernel API uses mezzio/mezzio-authentication-oauth2 for OAuth2 authentication. +The package itself is secure, but it must be used properly: - Replace or update the default `admin` and `frontend` clients on production. - Update the `access` and `refresh` tokens to match your application's requirements (defaults are one day for access, one month for refresh). @@ -88,7 +100,8 @@ OAuth 2.0 is a secure authorization framework letting one application access res ## Session and Cookie Settings -Sessions and cookies store data between HTTP requests, such as login information, preferences, or user behavior tracking. Dotkernel configures cookies in `config/autoload/session.global.php`, which contains parameters that must be revised and adapted: +Sessions and cookies store data between HTTP requests, such as login information, preferences, or user behavior tracking. +Dotkernel configures cookies in `config/autoload/session.global.php`, which contains parameters that must be revised and adapted: - `session_config.cookie_httponly` - `session_config.cookie_samesite` @@ -96,11 +109,17 @@ Sessions and cookies store data between HTTP requests, such as login information ## JavaScript Dependencies -JavaScript has its own dependencies, usually installed via npm or yarn. The JavaScript ecosystem has recently been attacked by hackers targeting several widely used npm packages with billions of total uses. Dotkernel uses npm to handle JavaScript dependencies, monitors the news for security issues, and uses packages from reliable sources. `npm audit` should still be used regularly to check for vulnerabilities. +JavaScript has its own dependencies, usually installed via npm or yarn. +The JavaScript ecosystem has recently been attacked by hackers targeting several widely used npm packages with billions of total uses. +Dotkernel uses npm to handle JavaScript dependencies, monitors the news for security issues, and uses packages from reliable sources. +`npm audit` should still be used regularly to check for vulnerabilities. ## Other Security Considerations -All components of Dotkernel Headless Platform have configuration files named `*.global.php`, `*.php.dist`, and `*.local.php`. Sensitive information must only go in `*.local.php` files, since they are ignored by the VCS by default. Development mode enables features like debug mode, cache clear, and error details, which should be hidden from production to avoid exposing sensitive data or code. The Laminas Continuous Integration GitHub Action is integral to Dotkernel API, running a matrix of static analysis, coding standards checks, and unit tests, most often triggered by commits. +All components of Dotkernel Headless Platform have configuration files named `*.global.php`, `*.php.dist`, and `*.local.php`. +Sensitive information must only go in `*.local.php` files, since they are ignored by the VCS by default. +Development mode enables features like debug mode, cache clear, and error details, which should be hidden from production to avoid exposing sensitive data or code. +The Laminas Continuous Integration GitHub Action is integral to Dotkernel API, running a matrix of static analysis, coding standards checks, and unit tests, most often triggered by commits. ## FAQ @@ -108,13 +127,16 @@ All components of Dotkernel Headless Platform have configuration files named `*. A: Software security spans many areas: authentication and access control, data protection, input validation and injection, web and API security, dependency and supply chain risks, configuration and deployment, network and infrastructure security, logging/monitoring and incident response, secure software development lifecycle, and human and organizational factors. **Q: How does Dotkernel handle form input validation?** -A: Dotkernel API uses laminas/laminas-inputfilter to ensure a field is present and of the correct type. Dotkernel Admin additionally uses laminas/laminas-form, which provides form element objects, an InputFilter for each input (or custom validators), and methods for binding data to and from the form, integrating with laminas-escaper, laminas-validator, laminas-session, and laminas-filter. +A: Dotkernel API uses laminas/laminas-inputfilter to ensure a field is present and of the correct type. +Dotkernel Admin additionally uses laminas/laminas-form, which provides form element objects, an InputFilter for each input (or custom validators), and methods for binding data to and from the form, integrating with laminas-escaper, laminas-validator, laminas-session, and laminas-filter. **Q: How does Dotkernel API handle content negotiation?** -A: Content negotiation is handled via a middleware configured in the `config/autoload/content-negotiation.global.php` file. It uses the Content-Type and Accept HTTP request headers to negotiate with the client, returning application/json or application/hal+json data formats. +A: Content negotiation is handled via a middleware configured in the `config/autoload/content-negotiation.global.php` file. +It uses the Content-Type and Accept HTTP request headers to negotiate with the client, returning application/json or application/hal+json data formats. **Q: How is CORS handled and configured in Dotkernel API?** -A: CORS is handled by mezzio/mezzio-cors and configured in the `config/autoload/cors.local.php` file, validating calls using configuration items like origins, headers, max age, and credentials. When configuring the pipeline, the CorsMiddleware must be added before the RouteMiddleware. +A: CORS is handled by mezzio/mezzio-cors and configured in the `config/autoload/cors.local.php` file, validating calls using configuration items like origins, headers, max age, and credentials. +When configuring the pipeline, the CorsMiddleware must be added before the RouteMiddleware. **Q: What should be done with the demo credentials before going to production?** A: Demo credentials are provided for convenience during installation testing, but it is important to update or remove these accounts in your production environment. diff --git a/public/readme/best-practice/golden-rules-of-professional-php-coding.md b/public/llms-content/best-practice/golden-rules-of-professional-php-coding.md similarity index 82% rename from public/readme/best-practice/golden-rules-of-professional-php-coding.md rename to public/llms-content/best-practice/golden-rules-of-professional-php-coding.md index a0696a75..cb136561 100644 --- a/public/readme/best-practice/golden-rules-of-professional-php-coding.md +++ b/public/llms-content/best-practice/golden-rules-of-professional-php-coding.md @@ -23,8 +23,10 @@ language: "en" ```php #@TODO masterpiece by @smartguy, to quick fix the division by zero ``` -5. Each function must do a single task. If it logs in the user and records the login in a stats table, create a separate function for the "record the login" part - maybe even a distinct class for stats. -6. Use a version control system. SVN is NOT dead. +5. Each function must do a single task. +If it logs in the user and records the login in a stats table, create a separate function for the "record the login" part - maybe even a distinct class for stats. +6. Use a version control system. +SVN is NOT dead. 7. Use an IDE, such as Aptana 2, Aptana 3, Eclipse, or Zend Studio. 8. Know your IDE: code snippets, code assist, integration with Zend Framework, SVN integration, bug tracker integration, and so on. @@ -40,7 +42,8 @@ A: Fix every warning or notice that occurs, and regularly check the server's err A: Identify any temporary hack with a special mark, such as a `#@TODO` comment noting who added it and why. **Q: What is the rule about what a function should do?** -A: Each function must do a single task. For example, if you're logging in a user and also recording that login in a stats table, create a separate function (or even a distinct class) for the stats recording, rather than combining both tasks in one function. +A: Each function must do a single task. +For example, if you're logging in a user and also recording that login in a stats table, create a separate function (or even a distinct class) for the stats recording, rather than combining both tasks in one function. **Q: What tools does the article recommend for professional PHP development?** A: It recommends using a version control system (noting that SVN is not dead) and using an IDE such as Aptana 2, Aptana 3, Eclipse, or Zend Studio, and knowing your IDE's code snippets, code assist, Zend Framework integration, SVN integration, and bug tracker integration. diff --git a/public/readme/best-practice/htaccess-301-redirect-non-www-to-www.md b/public/llms-content/best-practice/htaccess-301-redirect-non-www-to-www.md similarity index 100% rename from public/readme/best-practice/htaccess-301-redirect-non-www-to-www.md rename to public/llms-content/best-practice/htaccess-301-redirect-non-www-to-www.md diff --git a/public/readme/best-practice/insert-update-delete-statements-with-zend-db.md b/public/llms-content/best-practice/insert-update-delete-statements-with-zend-db.md similarity index 87% rename from public/readme/best-practice/insert-update-delete-statements-with-zend-db.md rename to public/llms-content/best-practice/insert-update-delete-statements-with-zend-db.md index c2d930a4..72b5e8f8 100644 --- a/public/readme/best-practice/insert-update-delete-statements-with-zend-db.md +++ b/public/llms-content/best-practice/insert-update-delete-statements-with-zend-db.md @@ -12,7 +12,8 @@ language: "en" ## TL;DR -DML (Data Manipulation Language) statements change data values in database tables. This article, continuing the Zend_Db series, shows how the three primary DML statements — INSERT, UPDATE, and DELETE — are written in raw SQL and translated into Zend_Db method calls. +DML (Data Manipulation Language) statements change data values in database tables. +This article, continuing the Zend_Db series, shows how the three primary DML statements — INSERT, UPDATE, and DELETE — are written in raw SQL and translated into Zend_Db method calls. ## Connecting to the database @@ -80,10 +81,12 @@ $db->delete('user', 'id = '.$id); ## FAQ **Q: What are DML statements?** -A: DML (Data Manipulation Language) statements are statements that change data values in database tables. There are 3 primary DML statements: INSERT, UPDATE, and DELETE. +A: DML (Data Manipulation Language) statements are statements that change data values in database tables. +There are 3 primary DML statements: INSERT, UPDATE, and DELETE. **Q: How do you insert a new row with Zend_Db?** -A: Build an associative array of column names to values (e.g. email, password, firstName, lastName, active) and pass it to $db->insert('user', $data), which corresponds to an SQL INSERT INTO ... VALUES statement. +A: Build an associative array of column names to values (e.g. email, password, firstName, lastName, active) and pass it to $db->insert('user', $data), which corresponds to an SQL INSERT INTO ... +VALUES statement. **Q: How do you update rows with Zend_Db, including incrementing a column?** A: Build a $data array of the columns to update, using a Zend_Db_Expr for expressions such as incrementing accountUpdate (new Zend_Db_Expr('accountUpdate+1')), then call $db->update('user', $data, 'id = '.$id). diff --git a/public/readme/best-practice/sql-queries-using-zend-db-select.md b/public/llms-content/best-practice/sql-queries-using-zend-db-select.md similarity index 92% rename from public/readme/best-practice/sql-queries-using-zend-db-select.md rename to public/llms-content/best-practice/sql-queries-using-zend-db-select.md index 12e8e806..bf96279f 100644 --- a/public/readme/best-practice/sql-queries-using-zend-db-select.md +++ b/public/llms-content/best-practice/sql-queries-using-zend-db-select.md @@ -12,7 +12,8 @@ language: "en" ## TL;DR -Zend_Db and its related classes provide a simple SQL database interface for Zend Framework. This article shows how classical SELECT queries with JOINs and WHERE IN clauses are translated into Zend_Db's select() style, and how to debug the generated query. +Zend_Db and its related classes provide a simple SQL database interface for Zend Framework. +This article shows how classical SELECT queries with JOINs and WHERE IN clauses are translated into Zend_Db's select() style, and how to debug the generated query. ## Connecting to the database @@ -106,7 +107,8 @@ echo $select->__toString();exit; ## FAQ **Q: What does Zend_Db provide?** -A: Zend_Db and its related classes provide a simple SQL database interface for Zend Framework. To connect to a MySQL database, the Pdo_Mysql adapter is used via Zend_Db::factory('Pdo_Mysql', $dbConnect). +A: Zend_Db and its related classes provide a simple SQL database interface for Zend Framework. +To connect to a MySQL database, the Pdo_Mysql adapter is used via Zend_Db::factory('Pdo_Mysql', $dbConnect). **Q: How do you write a SELECT with a JOIN and a WHERE clause in Zend_Db style?** A: Use $db->select()->from(array('a'=>'users'), array('a.id','a.name'))->join(array('b'=>'orders'), 'a.id = b.user_id', array('b.order_id'))->where('a.id = ?', $userId), which is equivalent to a classical SQL query using INNER JOIN. diff --git a/public/readme/best-practice/subqueries-with-zend-db.md b/public/llms-content/best-practice/subqueries-with-zend-db.md similarity index 100% rename from public/readme/best-practice/subqueries-with-zend-db.md rename to public/llms-content/best-practice/subqueries-with-zend-db.md diff --git a/public/readme/best-practice/svn-export-in-a-virtual-host.md b/public/llms-content/best-practice/svn-export-in-a-virtual-host.md similarity index 85% rename from public/readme/best-practice/svn-export-in-a-virtual-host.md rename to public/llms-content/best-practice/svn-export-in-a-virtual-host.md index a0fa6064..1fcbebfd 100644 --- a/public/readme/best-practice/svn-export-in-a-virtual-host.md +++ b/public/llms-content/best-practice/svn-export-in-a-virtual-host.md @@ -12,11 +12,13 @@ language: "en" ## TL;DR -`svn export` lets you export the contents of a repository into a virtual host directory. The commands should be run in a terminal (e.g. via Putty on Windows) on the target host, ideally using the domain's own user rather than root. +`svn export` lets you export the contents of a repository into a virtual host directory. +The commands should be run in a terminal (e.g. via Putty on Windows) on the target host, ideally using the domain's own user rather than root. ## Steps -1. Make sure Subversion is installed on the host by running `svn --version`. If you don't get a "command not found" message, it's installed; otherwise, install it. +1. Make sure Subversion is installed on the host by running `svn --version`. +If you don't get a "command not found" message, it's installed; otherwise, install it. 2. Go to the directory where you want to export the contents of the repository (e.g. `cd /var/www/vhosts/example.com/httpdocs` or `cd /home/sitename/public_html`). 3. Run the export command: @@ -56,7 +58,8 @@ chown -R siteuser.psacln /var/www/vhosts/example.com/httpdocs ## FAQ **Q: How do you check if Subversion is installed on the host?** -A: Run svn --version. If you don't get a "command not found" message, Subversion is installed; otherwise, you need to install it. +A: Run svn --version. +If you don't get a "command not found" message, Subversion is installed; otherwise, you need to install it. **Q: What is the basic command to export a repository?** A: The command is svn export repositoryUrl targetDirectory, run from the host where you want to export the repository, ideally using the domain's user rather than root. @@ -65,7 +68,8 @@ A: The command is svn export repositoryUrl targetDirectory, run from the host wh A: -r revisionNumber is optional and exports a specific revision; by default, the latest revision is used. **Q: What does the --force option do, and what is the risk?** -A: By default SVN will not export into an existing directory; --force overrides this. Be careful, since this option can overwrite files. +A: By default SVN will not export into an existing directory; --force overrides this. +Be careful, since this option can overwrite files. **Q: How do you fix file permissions if you exported the repository as a different user?** A: As root, run chown -R siteuser.psacln /var/www/vhosts/example.com/httpdocs to change the permissions back. diff --git a/public/readme/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md b/public/llms-content/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md similarity index 97% rename from public/readme/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md rename to public/llms-content/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md index 306ecb24..c3ed2522 100644 --- a/public/readme/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md +++ b/public/llms-content/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md @@ -12,7 +12,8 @@ language: "en" ## TL;DR -For better integration between SVN, the Zend Studio PHP IDE, and a bug tracker, a set of SVN properties must be set for each project. This article lists which properties to set and how. +For better integration between SVN, the Zend Studio PHP IDE, and a bug tracker, a set of SVN properties must be set for each project. +This article lists which properties to set and how. ## Steps diff --git a/public/readme/best-practice/using-like-wildcards-with-zend-db.md b/public/llms-content/best-practice/using-like-wildcards-with-zend-db.md similarity index 89% rename from public/readme/best-practice/using-like-wildcards-with-zend-db.md rename to public/llms-content/best-practice/using-like-wildcards-with-zend-db.md index 0d659298..5c56659d 100644 --- a/public/readme/best-practice/using-like-wildcards-with-zend-db.md +++ b/public/llms-content/best-practice/using-like-wildcards-with-zend-db.md @@ -12,7 +12,9 @@ language: "en" ## TL;DR -The LIKE condition allows pattern matching in the WHERE clause of SELECT, INSERT, UPDATE, or DELETE statements. The `_` wildcard matches a single character, and `%` matches any string of any length (including zero). This article shows how to use LIKE and NOT LIKE with both wildcards in Zend_Db. +The LIKE condition allows pattern matching in the WHERE clause of SELECT, INSERT, UPDATE, or DELETE statements. +The `_` wildcard matches a single character, and `%` matches any string of any length (including zero). +This article shows how to use LIKE and NOT LIKE with both wildcards in Zend_Db. ## Connecting to the database @@ -145,10 +147,12 @@ A: The _ wildcard matches a single character, while % matches any string of any A: LIKE allows pattern matching in the WHERE clause and can be used in any valid SQL statement: SELECT, INSERT, UPDATE, or DELETE. **Q: How do you build a LIKE query with Zend_Db?** -A: Quote the column with $this->db->quoteIdentifier(), build the condition with $this->db->quoteInto("$col LIKE ? ", $pattern), and pass the resulting $where string into ->where() on a select, then run it with $this->db->fetchAll($select). +A: Quote the column with $this->db->quoteIdentifier(), build the condition with $this->db->quoteInto("$col LIKE ? +", $pattern), and pass the resulting $where string into ->where() on a select, then run it with $this->db->fetchAll($select). **Q: How do you combine multiple LIKE conditions with OR?** -A: Build the first condition with quoteInto, then append further ones with quoteInto("OR $col LIKE (?) ", $pattern), as in the example matching 'gallery' or 'folder' in the source field. +A: Build the first condition with quoteInto, then append further ones with quoteInto("OR $col LIKE (?) +", $pattern), as in the example matching 'gallery' or 'folder' in the source field. **Q: How does NOT LIKE differ from LIKE?** A: NOT LIKE negates the pattern match — for example, id NOT LIKE '1_' returns ids that don't start with 1 or don't have exactly 2 digits, and NOT LIKE conditions can be chained with AND to exclude several patterns at once. diff --git a/public/llms-content/best-practice/what-are-returning-the-fetch-functions-from-zend-db.md b/public/llms-content/best-practice/what-are-returning-the-fetch-functions-from-zend-db.md new file mode 100644 index 00000000..cbf3460d --- /dev/null +++ b/public/llms-content/best-practice/what-are-returning-the-fetch-functions-from-zend-db.md @@ -0,0 +1,186 @@ +--- +title: "What are returning the FETCH functions from Zend_Db" +description: "A side-by-side comparison of the legacy query()/next_record()/f() row-fetching style with the fetchAll, fetchAssoc, fetchCol, fetchOne, fetchPairs, and fetchRow methods of Zend_Db_Adapter_Abstract." +author: "Teo" +date_published: "2010-06-15" +canonical_url: "https://www.dotkernel.com/best-practice/what-are-returning-the-fetch-functions-from-zend-db/" +category: "Best Practice" +language: "en" +--- + +# What are returning the FETCH functions from Zend_Db + +## TL;DR + +Continuing the Zend_Db article series, this article walks through the FETCH methods available on Zend_Db_Adapter_Abstract: fetchAll, fetchAssoc, fetchCol, fetchOne, fetchPairs, and fetchRow. +Each method is shown next to the equivalent old-style code built on query(), next_record(), and f(), so the two approaches can be compared side by side. + +## Available FETCH Methods + +Continuing the Zend_Db article series, this article stops at the FETCH methods found in Zend_Db_Adapter_Abstract: + +```php +array fetchAll (string|Zend_Db_Select $sql, ...) +array fetchAssoc (string|Zend_Db_Select $sql, ...) +array fetchCol (string|Zend_Db_Select $sql, ...) +string fetchOne (string|Zend_Db_Select $sql, ...) +array fetchPairs (string|Zend_Db_Select $sql, ...) +array fetchRow (string|Zend_Db_Select $sql, ...) +``` + +To make it easier to follow, each example below shows the classical, old-style query first, followed by the equivalent query written in Zend_Db style. + +## Connecting to the Database + +Initialize the connection to the MySQL database: + +```php +$db = Zend_Db::factory('Pdo_Mysql', $dbConnect); +``` + +## Setting Up the Query + +Here is a SQL query that we want to fetch: + +```sql +$sql = "SELECT id, title FROM files"; +$db->query($sql) +``` + +Here is the same query written in Zend_Db style: + +```php +$select = $db->select() + ->from('files', array('id', 'title')) +``` + +Note: the old style of fetching shown below uses an older class. +Here's what you need to know about its methods: + +- `query()` is similar to `mysqli_query()` from the Mysqli PHP extension +- `next_record()` is similar to `mysqli_next_result()` from the Mysqli PHP extension +- `f()` retrieves the value of the column specified as a parameter + +## fetchAll + +Old style: + +```php +while($db->next_record()) +{ + $a[] = array( + 'id' => $db->f('id'), + 'title' => $db->f('title') + ); +} +``` + +Zend_Db style: + +```php +$a = $db->fetchAll($select); +``` + +## fetchAssoc + +Old style: + +```php +while($db->next_record()) +{ + $a = array( + 'id' => $db->f('id'), + 'title' => $db->f('title') + ); +} +``` + +Zend_Db style: + +```php +$a = $db->fetchAssoc($select); +``` + +## fetchCol + +Old style: + +```php +while($db->next_record()) +{ + $a[] = $db->f('id'); +} +``` + +Zend_Db style: + +```php +$a = $db->fetchCol($select); +``` + +## fetchOne + +Old style: + +```php +$db->next_record(); +$a = $db->f('id'); +``` + +Zend_Db style: + +```php +$a = $db->fetchOne($select); +``` + +## fetchPairs + +Old style: + +```php +while($db->next_record()) +{ + $a = $db->f('title'); +} +``` + +Zend_Db style: + +```php +$a = $db->fetchPairs($select); +``` + +## fetchRow + +Old style: + +```php +$db->next_record(); +$a = array( + 'id' => $db->f('id'), + 'title' => $db->f('title') + ); +``` + +Zend_Db style: + +```php +$a = $db->fetchRow($select); +``` + +## FAQ + +**Q: What FETCH methods are available in Zend_Db_Adapter_Abstract?** +A: The article covers fetchAll, fetchAssoc, fetchCol, fetchOne, fetchPairs, and fetchRow. + +**Q: What does fetchAll do compared to the old query style?** +A: `$a = $db->fetchAll($select)` replaces the old-style loop that calls `next_record()` repeatedly and builds an array of associative rows using `f()` for each column. + +**Q: What does fetchRow return?** +A: `$a = $db->fetchRow($select)` returns a single row as an associative array, replacing a single `next_record()` call followed by `f()` calls for each column. + +**Q: What does fetchOne return?** +A: `$a = $db->fetchOne($select)` returns a single value, replacing a single `next_record()` call followed by one `f()` call. + +**Q: How do the old-style query(), next_record(), and f() methods relate to Mysqli?** +A: `query()` is similar to `mysqli_query()`, `next_record()` is similar to `mysqli_next_result()`, and `f()` retrieves the value of the column specified as a parameter. diff --git a/public/llms-content/best-practice/why-use-current-timestamp-on-a-field-that-record-date-time.md b/public/llms-content/best-practice/why-use-current-timestamp-on-a-field-that-record-date-time.md new file mode 100644 index 00000000..123bc0ef --- /dev/null +++ b/public/llms-content/best-practice/why-use-current-timestamp-on-a-field-that-record-date-time.md @@ -0,0 +1,67 @@ +--- +title: "Why use CURRENT_TIMESTAMP on a field that record date/time?" +description: "Why a TIMESTAMP column should default to CURRENT_TIMESTAMP on insert, how ON UPDATE CURRENT_TIMESTAMP keeps it fresh on every update, and how the DEFAULT/ON UPDATE clause combinations behave." +author: "Teo" +date_published: "2010-06-29" +canonical_url: "https://www.dotkernel.com/best-practice/why-use-current-timestamp-on-a-field-that-record-date-time/" +category: "Best Practice" +language: "en" +--- + +# Why use CURRENT_TIMESTAMP on a field that record date/time? + +## TL;DR + +On a TIMESTAMP field that records date and time when inserting a new record, it's encouraged to use the CURRENT_TIMESTAMP constant as its DEFAULT value. +This removes the need to set the value manually from PHP or with MySQL's NOW() function, and the ON UPDATE CURRENT_TIMESTAMP clause can additionally keep the field updated automatically on every row update. +Only one TIMESTAMP field per table can be DEFAULT CURRENT_TIMESTAMP. + +## Why Use CURRENT_TIMESTAMP as a Default + +On a TIMESTAMP field that records date and time when inserting a new record, it is encouraged to use the CURRENT_TIMESTAMP constant as a DEFAULT value. +Because when inserting a new row in the table, there is no need to specifically add the value for the date and time field, either by creating it from PHP code with the Date/Time functions or with MySQL's NOW() function: + +```sql +ALTER TABLE `user` CHANGE `dateCreated` `dateCreated` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; +``` + +## Automatically Updating with ON UPDATE CURRENT_TIMESTAMP + +CURRENT_TIMESTAMP is also a solution for updating date and time fields. +Use the `ON UPDATE CURRENT_TIMESTAMP` clause if you want the value of the field to be changed automatically each time the row is updated: + +```sql +ALTER TABLE `user` CHANGE `dateLogin` `dateLogin` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; +``` + +## DEFAULT and ON UPDATE Clause Combinations + +DEFAULT and ON UPDATE clauses can be used together or separately, depending on your needs: + +- With both `DEFAULT CURRENT_TIMESTAMP` and `ON UPDATE CURRENT_TIMESTAMP` clauses, the column has the current timestamp for its default value and is automatically updated. +- With neither `DEFAULT` nor `ON UPDATE` clauses, it is the same as `DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP` (only for the first TIMESTAMP field in the table). +- With a `DEFAULT CURRENT_TIMESTAMP` clause and no `ON UPDATE` clause, the column has the current timestamp for its default value but is not automatically updated. +- With no `DEFAULT` clause and with an `ON UPDATE CURRENT_TIMESTAMP` clause, the column has a default of 0 and is automatically updated. +- With a constant `DEFAULT` value, the column has the given default and is not automatically initialized to the current timestamp. +If the column also has an `ON UPDATE CURRENT_TIMESTAMP` clause, it is automatically updated; otherwise, it has a constant default and is not automatically updated. + +For more details, check out the MySQL Manual. + +Note: only one timestamp field can be `DEFAULT CURRENT_TIMESTAMP` in a table. + +## FAQ + +**Q: Why use CURRENT_TIMESTAMP as a DEFAULT value for a date/time field?** +A: Because when inserting a new row, there is no need to specifically set the date/time value yourself, either from PHP Date/Time functions or with MySQL's NOW() function. + +**Q: How do you make a field update its timestamp automatically on every UPDATE?** +A: Add the ON UPDATE CURRENT_TIMESTAMP clause, for example: `ALTER TABLE `user` CHANGE `dateLogin` `dateLogin` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP`. + +**Q: What happens if a TIMESTAMP column has neither a DEFAULT nor an ON UPDATE clause?** +A: For the first TIMESTAMP field in the table, having neither clause is the same as DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP. + +**Q: What happens with a DEFAULT CURRENT_TIMESTAMP clause but no ON UPDATE clause?** +A: The column gets the current timestamp as its default value but is not automatically updated afterward. + +**Q: Can more than one TIMESTAMP column default to CURRENT_TIMESTAMP in the same table?** +A: No. Only one timestamp field in a table can be DEFAULT CURRENT_TIMESTAMP. diff --git a/public/llms-content/best-practice/zf-is-retired-laminas-mvc-is-retiring-consider-it-solved.md b/public/llms-content/best-practice/zf-is-retired-laminas-mvc-is-retiring-consider-it-solved.md new file mode 100644 index 00000000..3a5e4f84 --- /dev/null +++ b/public/llms-content/best-practice/zf-is-retired-laminas-mvc-is-retiring-consider-it-solved.md @@ -0,0 +1,94 @@ +--- +title: "ZF Is Retired. Laminas MVC Is Retiring. Consider It Solved" +description: "What the Laminas MVC retirement announcement means, why legacy MVC platforms are a liability, and how Apidemia helps teams migrate to the Mezzio middleware architecture." +author: "Florin Bidirean" +date_published: "2025-07-17" +canonical_url: "https://www.dotkernel.com/best-practice/zf-is-retired-laminas-mvc-is-retiring-consider-it-solved/" +category: "Best Practice" +language: "en" +--- + +# ZF Is Retired. Laminas MVC Is Retiring. Consider It Solved + +## TL;DR + +Laminas MVC is retiring, following Zend Framework and Apigility before it, but this doesn't mean everything with a Laminas logo is going away — Mezzio, built on Laminas components, is the fully-functional successor. +Maintaining legacy MVC platforms is costly and risky long-term, since the architecture of today and tomorrow is middleware-based, and Apidemia offers a proven, phased migration process to move legacy platforms to Mezzio. + +## A Bit of History + +It all started with the announcement: Laminas MVC Is Retiring. +Some people wrongfully thought everything with a Laminas logo is going away — not so. +Read on for a bit of history about Zend and Laminas, what it means to migrate your platform, and why it's a decision that should not be taken lightly. + +Laminas MVC is not even the first framework that has reached its end of life — look at Zend Framework and Apigility. +Letting go of a flagship product is a difficult decision, but it's made easier when you leave a solid alternative in its wake. +The developers who worked on Laminas MVC already had something better and fully-functional in place — the Mezzio microframework, built using Laminas components. +It has itself gone through rigorous development and testing since being released in 2015, when it was known as Zend Expressive, then was renamed into Mezzio to get to its current state. + +## What Is the Issue with Legacy Platforms? + +Maintaining legacy platforms over the long term is often a costly and time-consuming endeavour. +Every few years, platform owners must consider the viability of migrating to a newer platform. + +Newer platforms implement modern architectures, have an active community, and are actively being developed and maintained. +They also offer easier development, expansion, and maintenance, alongside vital security improvements and more reliable dependencies. + +Sounds like an easy decision? Sure, but it's a lot of work, and that's when the specialists come into play. + +We at Apidemia have been using the Zend Framework, Laminas MVC, and Mezzio for years. +We understand their ins-and-outs intimately, which enables us to analyze and perform the transfer of a legacy platform to Mezzio effectively. +Working with Mezzio ensures faster execution times, increased security, faster development, and long-term reliability from all points of view. +We encourage this change and are ready to offer guidance. + +## Pain Points + +The MVC architecture is obsolete. +It is yesterday's architecture, fit for monolithic websites. +The architecture of today and tomorrow is based on middleware, building headless platforms, websites, and microservices following the same coding approach. + +| Pain Point | Apidemia Solution | +|---|---| +| Legacy framework is deprecated and/or has no long-term support | Apidemia helps migrate to modern middleware architecture (Mezzio microframework with Laminas components) | +| Legacy applications are hard to maintain | Modern architecture improves code quality, testability and performance | +| Migration is risky or expensive | Apidemia uses a proven, phased migration strategy to reduce risk | +| Lack of internal development expertise | Apidemia provides end-to-end guidance, refactoring, training and support | + +## How Apidemia Handles Migrations + +Apidemia has created a complex process that involves several steps to ensure a smooth migration. +In a nutshell, the current project functionality must be understood, and only then can the move be implemented into the destination platform. +Over the long run, the Apidemia team offers support and training. + +This is the simplified task list: + +- Code audit & migration strategy — to understand the code and see what goes where. +- Partial or full migration to Laminas or PSR-compliant frameworks, like Mezzio or Symfony — this decision impacts both time to implement and cost, negotiated with the client. +- Refactoring and decoupling legacy modules — the old code must go and be replaced with the new. +- Unit testing and CI/CD pipeline setup — a vital step to ensure things function the same way in the destination platform. +- Post-migration support and team training — this step depends on the level of collaboration between the original developers and the Apidemia team, so the more closely they work together, the easier it is to onboard the devs for the long run. + +## FAQ + +**Q: What is Laminas MVC being replaced by?** +A: Mezzio microframework, built using Laminas components. It has gone through rigorous development and testing since being released in 2015, when it was known as Zend Expressive, before being renamed Mezzio. + +**Q: Why is maintaining legacy platforms a problem?** +A: Maintaining legacy platforms over the long term is often costly and time-consuming, so every few years platform owners must consider migrating to a newer platform that offers modern architecture, an active community, easier development/expansion/maintenance, security improvements, and more reliable dependencies. + +**Q: What is the core architectural pain point with legacy MVC platforms?** +A: The MVC architecture is obsolete, fit for monolithic websites. Today's and tomorrow's architecture is based on middleware, building headless platforms, websites, and microservices following the same coding approach. + +**Q: What steps does Apidemia's migration process involve?** +A: A simplified task list: code audit & migration strategy; partial or full migration to Laminas or PSR-compliant frameworks like Mezzio or Symfony; refactoring and decoupling legacy modules; unit testing and CI/CD pipeline setup; and post-migration support and team training. + +**Q: Who offers this migration guidance?** +A: Apidemia, who has used Zend Framework, Laminas MVC, and Mezzio for years and can analyze and perform the transfer of a legacy platform to Mezzio, offering faster execution times, increased security, faster development, and long-term reliability. + +## Resources + +- [Dotkernel Headless Platform](https://www.dotkernel.com/headless-platform/dotkernel-headless-platform-the-whats-hows-and-whys/) +- [Shared Core Submodule in Dotkernel Headless Platform](https://www.dotkernel.com/headless-platform/shared-core-submodule-in-dotkernel-headless-platform/) +- [Understanding Middleware](https://www.dotkernel.com/architecture/understanding-middleware/) +- [Dotkernel Light](https://www.dotkernel.com/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components/) +- [Migrate Laminas MVC to Dotkernel](https://www.apidemia.com/services/migrate-laminas-mvc-to-dotkernel/) diff --git a/public/readme/dotkernel-api/api-client-migration-from-postman-to-bruno.md b/public/llms-content/dotkernel-api/api-client-migration-from-postman-to-bruno.md similarity index 70% rename from public/readme/dotkernel-api/api-client-migration-from-postman-to-bruno.md rename to public/llms-content/dotkernel-api/api-client-migration-from-postman-to-bruno.md index 4f0d7e8a..f26149bf 100644 --- a/public/readme/dotkernel-api/api-client-migration-from-postman-to-bruno.md +++ b/public/llms-content/dotkernel-api/api-client-migration-from-postman-to-bruno.md @@ -12,11 +12,13 @@ language: "en" ## TL;DR -The team has used Postman for years but is considering switching to Bruno, a lightweight, offline-first alternative, reflecting a broader PHP community trend toward local-first, Git-native developer tools. Bruno wins on offline access, version control via Git, performance, and (arguably) security, while Postman still offers a broader feature set for larger, budget-having teams. +The team has used Postman for years but is considering switching to Bruno, a lightweight, offline-first alternative, reflecting a broader PHP community trend toward local-first, Git-native developer tools. +Bruno wins on offline access, version control via Git, performance, and (arguably) security, while Postman still offers a broader feature set for larger, budget-having teams. ## Why We Switched to the Offline-Focused Bruno -Every API developer needs a reliable client for testing and interacting with the API — ideally free, able to store and share endpoint collections easily with a team, fast, and secure. Postman has been the team's go-to tool for years, but they are now considering Bruno, part of a general trend in the PHP community toward local-first, Git-native developer tools. +Every API developer needs a reliable client for testing and interacting with the API — ideally free, able to store and share endpoint collections easily with a team, fast, and secure. +Postman has been the team's go-to tool for years, but they are now considering Bruno, part of a general trend in the PHP community toward local-first, Git-native developer tools. ## Comparing Postman to Bruno @@ -31,7 +33,8 @@ Every API developer needs a reliable client for testing and interacting with the ### Comparison Conclusion -Postman is currently a better fit for larger teams willing to allocate a budget for a more feature-rich platform. Bruno stores collections in Git, so everything is offline, which the team considers more secure while also being generally faster. +Postman is currently a better fit for larger teams willing to allocate a budget for a more feature-rich platform. +Bruno stores collections in Git, so everything is offline, which the team considers more secure while also being generally faster. ## Alternative API Clients @@ -48,35 +51,43 @@ Any of them can get the job done; the decision comes down to choosing a simple, ## Bruno for Dotkernel -Bruno currently seems like the best match for the team, offering similar functionality to Postman plus the ability to work completely offline and save endpoint collections to their GitHub accounts. The offline feature weighed most heavily in the decision. +Bruno currently seems like the best match for the team, offering similar functionality to Postman plus the ability to work completely offline and save endpoint collections to their GitHub accounts. +The offline feature weighed most heavily in the decision. ### Tool Migration -Since most of the team has only worked with Postman, switching tools can affect efficiency at first, and tool migration can have an emotional impact as developers relearn a new tool's ins and outs. Given Bruno's straightforward approach and reasonable learning curve, the team expects this to be mitigated easily, and views the switch as an expansion of their expertise that avoids getting tied to one tool. +Since most of the team has only worked with Postman, switching tools can affect efficiency at first, and tool migration can have an emotional impact as developers relearn a new tool's ins and outs. +Given Bruno's straightforward approach and reasonable learning curve, the team expects this to be mitigated easily, and views the switch as an expansion of their expertise that avoids getting tied to one tool. ### How Long Will Bruno Last? -The team expects Bruno may eventually restrict developers with paid plans too, just like Postman did, but plans to cross that bridge when they get to it. For now, Bruno is becoming their de facto API client, and the whole team is being encouraged to adopt it as soon as possible. +The team expects Bruno may eventually restrict developers with paid plans too, just like Postman did, but plans to cross that bridge when they get to it. +For now, Bruno is becoming their de facto API client, and the whole team is being encouraged to adopt it as soon as possible. ## FAQ **Q: Why is the team considering a switch from Postman to Bruno?** -A: They want a reliable API testing client that is free, stores and shares endpoint collections easily with the team, and is fast and secure. This reflects a broader trend in the PHP community toward local-first, Git-native developer tools. +A: They want a reliable API testing client that is free, stores and shares endpoint collections easily with the team, and is fast and secure. +This reflects a broader trend in the PHP community toward local-first, Git-native developer tools. **Q: What is the main architectural difference between Postman and Bruno?** A: Postman's free plan now only allows one user account, while Bruno offers a fully-offline experience based on shared `.bru` files, so there is no restriction on the number of developers using them. **Q: How does version control differ between the two tools?** -A: Postman stores collections and handles version control in the cloud, forcing developers to stay online (though collections can be exported/imported via its UI). Bruno's `.bru` files can be saved directly in a Git repository and are version-controlled through Git like any other project file. +A: Postman stores collections and handles version control in the cloud, forcing developers to stay online (though collections can be exported/imported via its UI). +Bruno's `.bru` files can be saved directly in a Git repository and are version-controlled through Git like any other project file. **Q: How does performance compare between Postman and Bruno?** -A: Bruno is the clear winner on performance: it uses much less RAM and is generally faster. Postman needs to regularly synchronize with the cloud and store its advanced features in RAM, which can introduce delays. +A: Bruno is the clear winner on performance: it uses much less RAM and is generally faster. +Postman needs to regularly synchronize with the cloud and store its advanced features in RAM, which can introduce delays. **Q: Is Bruno more secure than Postman?** -A: Postman offers Single Sign-On (SSO) and Role-Based Access Control (RBAC), which the team doesn't find useful for its workflow. Bruno's local files never leave the dev environment, which the article argues makes it more secure, especially for avoiding sharing client files with online tools. +A: Postman offers Single Sign-On (SSO) and Role-Based Access Control (RBAC), which the team doesn't find useful for its workflow. +Bruno's local files never leave the dev environment, which the article argues makes it more secure, especially for avoiding sharing client files with online tools. **Q: What's the overall conclusion on Postman versus Bruno?** -A: Postman is currently a better fit for larger teams willing to allocate a budget for a more feature-rich platform. Bruno stores collections in Git so everything works offline, which the team considers more secure while also being generally faster, and it has become their de facto API client. +A: Postman is currently a better fit for larger teams willing to allocate a budget for a more feature-rich platform. +Bruno stores collections in Git so everything works offline, which the team considers more secure while also being generally faster, and it has become their de facto API client. ## Resources diff --git a/public/readme/dotkernel-api/api-endpoint-to-collect-client-errors.md b/public/llms-content/dotkernel-api/api-endpoint-to-collect-client-errors.md similarity index 87% rename from public/readme/dotkernel-api/api-endpoint-to-collect-client-errors.md rename to public/llms-content/dotkernel-api/api-endpoint-to-collect-client-errors.md index e1b4d39b..53cc2060 100644 --- a/public/readme/dotkernel-api/api-endpoint-to-collect-client-errors.md +++ b/public/llms-content/dotkernel-api/api-endpoint-to-collect-client-errors.md @@ -10,7 +10,8 @@ language: "en" # API Endpoint to Collect Client Errors -When a Frontend (e.g. Angular) sits on top of a Dotkernel API, errors can happen - the API's response may have changed overnight, or a variable may simply be `undefined`. Since the Frontend runs on the user's own client, there's little that can be done about it directly, so an endpoint was created to let clients submit the error message when something goes wrong. +When a Frontend (e.g. Angular) sits on top of a Dotkernel API, errors can happen - the API's response may have changed overnight, or a variable may simply be `undefined`. +Since the Frontend runs on the user's own client, there's little that can be done about it directly, so an endpoint was created to let clients submit the error message when something goes wrong. ## Usage diff --git a/public/readme/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md b/public/llms-content/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md similarity index 72% rename from public/readme/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md rename to public/llms-content/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md index fc4bf26c..096a05cd 100644 --- a/public/readme/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md +++ b/public/llms-content/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md @@ -12,11 +12,13 @@ language: "en" ## TL;DR -Content negotiation lets clients and servers agree on the format and language of exchanged data. It can be handled server-side or client-side (the latter being more versatile), communicated through HTTP headers or URL patterns, and Dotkernel API implements it out of the box using the `Content-Type` and `Accept` headers. +Content negotiation lets clients and servers agree on the format and language of exchanged data. +It can be handled server-side or client-side (the latter being more versatile), communicated through HTTP headers or URL patterns, and Dotkernel API implements it out of the box using the `Content-Type` and `Accept` headers. ## What is the Purpose of Content Negotiation? -RESTful resources can support multiple representations, and efficient client-server communication depends on both sides agreeing on the exchanged data format - this agreement is content negotiation. It ensures: +RESTful resources can support multiple representations, and efficient client-server communication depends on both sides agreeing on the exchanged data format - this agreement is content negotiation. +It ensures: - **Support for diverse clients**, e.g. `Accept: application/json` or `Accept: application/xml`. - **Data format flexibility**, e.g. using `Accept: application/msgpack` (a binary serialization) instead of JSON for a smaller, easier-to-transfer response. @@ -26,14 +28,17 @@ RESTful resources can support multiple representations, and efficient client-ser Either the client or the server can decide: -- **Server-side negotiation**: the server decides the format based on various factors. This can introduce erroneous assumptions and a more complex server-side implementation, and forces the client to adhere to the server's rules. -- **Client-side negotiation**: the client tells the server what format it prefers. This approach is more versatile and makes more sense. +- **Server-side negotiation**: the server decides the format based on various factors. +This can introduce erroneous assumptions and a more complex server-side implementation, and forces the client to adhere to the server's rules. +- **Client-side negotiation**: the client tells the server what format it prefers. +This approach is more versatile and makes more sense. There are two ways to communicate the preferred data format: HTTP request headers, or resource URI patterns. ### HTTP Request Headers -The `Content-Type` and `Accept` headers determine the data format sent in the request and response. Examples of types include `text/plain`, `text/html`, `application/json`, `application/zip`, `image/gif`, and `image/jpeg`. +The `Content-Type` and `Accept` headers determine the data format sent in the request and response. +Examples of types include `text/plain`, `text/html`, `application/json`, `application/zip`, `image/gif`, and `image/jpeg`. ```shell Content-Type: application/json, text/plain @@ -70,7 +75,8 @@ In this example, the client accepts both JSON and XML, with JSON preferred. If t ## How Does Dotkernel API Handle Content Negotiation? -Out of the box, Dotkernel API uses the `Content-Type` and `Accept` HTTP request headers to handle client-side content negotiation, supporting both `application/json` and `application/hal+json`. These can be changed as development progresses, and per-route content negotiation is also supported. Configuration lives in its own configuration file, validation is automatic, and several explicit errors are handled based on the supported format. +Out of the box, Dotkernel API uses the `Content-Type` and `Accept` HTTP request headers to handle client-side content negotiation, supporting both `application/json` and `application/hal+json`. +These can be changed as development progresses, and per-route content negotiation is also supported. Configuration lives in its own configuration file, validation is automatic, and several explicit errors are handled based on the supported format. ## FAQ @@ -81,16 +87,21 @@ A: It's the act of a client and server agreeing on the format and language of th A: It ensures support for diverse clients (e.g. `Accept: application/json` or `Accept: application/xml`), data format flexibility for smaller responses (e.g. `Accept: application/msgpack`, a binary serialization), and language localization via headers like `Accept-Language: en-US`. **Q: Who decides the data format, the client or the server?** -A: Either side technically can. In server-side negotiation, the server decides based on various factors, which can introduce erroneous assumptions and more complex implementation, forcing the client to adhere to server rules. In client-side negotiation, the client tells the server what format it prefers, which is more versatile and makes more sense. +A: Either side technically can. +In server-side negotiation, the server decides based on various factors, which can introduce erroneous assumptions and more complex implementation, forcing the client to adhere to server rules. +In client-side negotiation, the client tells the server what format it prefers, which is more versatile and makes more sense. **Q: How can the preferred data format be communicated?** -A: Via HTTP request headers (`Content-Type` and `Accept`) or via resource URI patterns, such as a file extension in the URL (e.g. `/record/47.json`) or an extra query parameter (e.g. `/record/47?format=json`). If the `Accept` header is not present, the server decides the response format. +A: Via HTTP request headers (`Content-Type` and `Accept`) or via resource URI patterns, such as a file extension in the URL (e.g. `/record/47.json`) or an extra query parameter (e.g. `/record/47?format=json`). +If the `Accept` header is not present, the server decides the response format. **Q: How does the quality factor (q) work in the Accept header?** -A: The `Accept` header can list multiple accepted formats with a `q` value between 0 and 1 to express preference, e.g. `Accept: application/json,application/xml;q=0.9,*/*;q=0.8`. The server responds with the most preferred format it can satisfy, falling back further down the list if needed. +A: The `Accept` header can list multiple accepted formats with a `q` value between 0 and 1 to express preference, e.g. `Accept: application/json,application/xml;q=0.9,*/*;q=0.8`. +The server responds with the most preferred format it can satisfy, falling back further down the list if needed. **Q: How does Dotkernel API handle content negotiation?** -A: Out of the box, Dotkernel API uses the `Content-Type` and `Accept` HTTP request headers to handle client-side content negotiation, supporting both `application/json` and `application/hal+json`. These can be changed as needed, and per-route content negotiation is also supported. +A: Out of the box, Dotkernel API uses the `Content-Type` and `Accept` HTTP request headers to handle client-side content negotiation, supporting both `application/json` and `application/hal+json`. +These can be changed as needed, and per-route content negotiation is also supported. ## Resources diff --git a/public/readme/dotkernel-api/dotkernel-api-1-0-0-released.md b/public/llms-content/dotkernel-api/dotkernel-api-1-0-0-released.md similarity index 100% rename from public/readme/dotkernel-api/dotkernel-api-1-0-0-released.md rename to public/llms-content/dotkernel-api/dotkernel-api-1-0-0-released.md diff --git a/public/readme/dotkernel-api/dotkernel-api-client-side-authorization.md b/public/llms-content/dotkernel-api/dotkernel-api-client-side-authorization.md similarity index 100% rename from public/readme/dotkernel-api/dotkernel-api-client-side-authorization.md rename to public/llms-content/dotkernel-api/dotkernel-api-client-side-authorization.md diff --git a/public/llms-content/dotkernel-api/dotkernel-api-server-side-authorization.md b/public/llms-content/dotkernel-api/dotkernel-api-server-side-authorization.md new file mode 100644 index 00000000..bdf874a1 --- /dev/null +++ b/public/llms-content/dotkernel-api/dotkernel-api-server-side-authorization.md @@ -0,0 +1,104 @@ +--- +title: "DotKernel API Server Side Authorization" +description: "How to configure server-side authorization in DotKernel API, covering no-auth, authentication and authorization access levels, role inheritance, and route permissions." +author: "Alex Karajos" +date_published: "2019-09-05" +canonical_url: "https://www.dotkernel.com/dotkernel-api/dotkernel-api-server-side-authorization/" +category: "Dotkernel API" +language: "en" +--- + +# DotKernel API Server Side Authorization + +## TL;DR + +DotKernel API endpoints can be protected at three levels: no-auth, authentication, and authorization. +Access is configured in `config/autoload/authorization.local.php` under the `zend-expressive-authorization-rbac` key, using a `roles` section for role inheritance and a `permissions` section for route access. +Authentication endpoints require a valid Bearer token and return `401 Unauthorized` if it's missing, while authorization endpoints additionally check role permissions and return `403 Forbidden`. + +This article covers the basic authorization of a Server Side application built using [DotKernel API](https://github.com/dotkernel/api). + +## Protecting an Endpoint + +- no-auth: the resource can be accessed without the need of authentication/authorization +- authentication: the resource can be accessed only by authenticated users +- authorization: the resource can be accessed only by authenticated AND authorized users + +Configuring access to the endpoints is done by editing the following config file: `config/autoload/authorization.local.php`. + +> Note: If this file is missing from your application, locate its dist file `config/autoload/authorization.local.php.dist` and copy it as the above-mentioned `config/autoload/authorization.local.php`. + +You should look for the array inside this config key: `zend-expressive-authorization-rbac`. + +```php +'zend-expressive-authorization-rbac' => , + 'member' => , + 'guest' => , + ], + 'permissions' => , + ], +] +``` + +Under the key roles you can define role inheritance. +In the above example: + +- admin inherits from no other role: `'admin' => []` +- member inherits from admin: `'member' =>` +- guest inherits from member: `'guest' =>` + +Of course, this setup is just a model, you should not use it in live projects because guests will end up having the same rights as admins. + +Under the key permissions you can define which routes are accessible to a role. +In the above example, a member has access to the routes named avatar, users and user. + +### 1. No-Auth Endpoints + +These endpoints can be accessed without authentication/authorization. +Examples could be: login, register, contact etc. +Creating a route for such an endpoint will use only the handler(s) responsible for returning the content: + +```php +$app->get('/users', UserHandler::class, 'users'); +``` + +### 2. Endpoints Requiring Authentication + +These endpoints can be accessed only if a valid `Bearer token` is present in the request headers. +Else, the API will return a `401 Unauthorized` response. +Creating a route for such an endpoint will have a structure similar to the following example: + +```php +$app->get('/users', , 'users'); +``` + +### 3. Endpoints Requiring Authorization + +These endpoints can be accessed only if a valid `Bearer token` is present in the request headers. +Else, the API will return a `403 Forbidden` response. +Creating a route for such an endpoint will have a structure similar to the following example: + +```php +$app->get('/users', , 'users'); +``` + +## FAQ + +**Q: What are the three access levels for protecting an endpoint?** +A: no-auth, where the resource can be accessed without authentication/authorization; authentication, where only authenticated users can access the resource; and authorization, where only authenticated AND authorized users can access it. + +**Q: Where do I configure access to the endpoints?** +A: In `config/autoload/authorization.local.php`. +If that file is missing from your application, locate its dist file `config/autoload/authorization.local.php.dist` and copy it as `config/autoload/authorization.local.php`, then look for the array under the `zend-expressive-authorization-rbac` config key. + +**Q: How does role inheritance work under the roles key?** +A: In the article's example, admin inherits from no other role, member inherits from admin, and guest inherits from member. +The article warns this exact setup is just a model and should not be used in live projects, because guests would end up having the same rights as admins. + +**Q: How do I control which routes a role can access?** +A: Under the `permissions` key you define which routes are accessible to a role. +In the article's example, a member has access to the routes named avatar, users, and user. + +**Q: What response codes are returned for authentication and authorization endpoints?** +A: Endpoints requiring authentication return a 401 Unauthorized response if a valid Bearer token isn't present in the request headers. +Endpoints requiring authorization return a 403 Forbidden response instead under the same condition. diff --git a/public/llms-content/dotkernel-api/dotkernel-api-versus-laminas-api-tools.md b/public/llms-content/dotkernel-api/dotkernel-api-versus-laminas-api-tools.md new file mode 100644 index 00000000..03a20da3 --- /dev/null +++ b/public/llms-content/dotkernel-api/dotkernel-api-versus-laminas-api-tools.md @@ -0,0 +1,65 @@ +--- +title: "DotKernel API versus Laminas API Tools" +description: "A feature-by-feature comparison of Laminas API Tools and Dotkernel API, showing why Dotkernel API is a solid alternative now that Laminas API Tools is archived." +author: "Florin Bidirean" +date_published: "2024-06-03" +canonical_url: "https://www.dotkernel.com/dotkernel-api/dotkernel-api-versus-laminas-api-tools/" +category: "Dotkernel API" +language: "en" +--- + +# DotKernel API versus Laminas API Tools + +## TL;DR + +This article compares the basic features of Laminas API Tools and Dotkernel API side by side, covering architecture, versioning, documentation, authentication, and more. +It highlights that Dotkernel API is a solid alternative now that Laminas API Tools has been archived, since Dotkernel API uses a modern middleware architecture, MIT license, and evolution-based deprecations instead of traditional versioning. + +Below is an analysis of the basic features available in Laminas API Tools and DotKernel API. +It's intended to highlight the differences between the two and also to showcase why DotKernel API is a good alternative for Laminas API Tools, especially considering the latter's archived status. + +> The table below refers to [Dotkernel API V7](https://github.com/dotkernel/api/tree/7.0). + +| | API Tools (formerly Apigility) | Dotkernel API | +|---|---|---| +| URL | [api-tools](https://api-tools.getlaminas.org/) | [Dotkernel API](https://www.dotkernel.org) | +| First Release | 2012 | 2018 | +| PHP Version | <= 8.2 | Shown via a dynamic Packagist badge (see the project repository for the current supported version) | +| Architecture | MVC, Event Driven | Middleware | +| OSS Lifecycle | Archived | Shown via a dynamic OSS Lifecycle badge (see the project repository for the current status) | +| Style | REST, RPC | REST | +| Versioning | Yes | Deprecations (API Evolution) * | +| Documentation | Swagger (Automated) | Postman (Manual), OpenAPI 3.0 (Swagger) | +| Content-Negotiation | Custom | Custom | +| License | BSD-3 | MIT | +| Default DB Layer | laminas-db | doctrine-orm 3.x | +| Authorization | ACL | RBAC-guard | +| Authentication | HTTP Basic/Digest OAuth2.0 | OAuth2.0 | +| CI/CD | Yes | Yes | +| Unit Tests | Yes | Yes | +| Code (Endpoint) Generator | Yes | [dot-maker](https://docs.dotkernel.org/dot-maker/v1/overview/) | +| PSR | PSR-7 | PSR-7, PSR-15 | + +## Note + +- Versioning is replaced by [Deprecations](https://docs.dotkernel.org/api-documentation/v6/tutorials/api-evolution/), using an evolution strategy. + +## FAQ + +**Q: What is the purpose of this comparison?** +A: It highlights the differences between Laminas API Tools and Dotkernel API, and shows why Dotkernel API is a good alternative now that Laminas API Tools is archived. + +**Q: Which version of Dotkernel API does the comparison table refer to?** +A: Dotkernel API V7. + +**Q: What architecture does each project use?** +A: Laminas API Tools uses an MVC, event-driven architecture, while Dotkernel API uses a middleware architecture. + +**Q: What license does each project use?** +A: Laminas API Tools is licensed under BSD-3, while Dotkernel API is licensed under MIT. + +**Q: How does Dotkernel API handle API versioning?** +A: Instead of traditional versioning, Dotkernel API replaces it with Deprecations, using an evolution (API Evolution) strategy. + +**Q: What documentation options does each project support?** +A: Laminas API Tools generates Swagger documentation automatically, while Dotkernel API supports manual Postman documentation as well as automated OpenAPI 3.0 (Swagger) documentation. diff --git a/public/llms-content/dotkernel-api/error-reporting-endpoint-in-dotkernel-api.md b/public/llms-content/dotkernel-api/error-reporting-endpoint-in-dotkernel-api.md new file mode 100644 index 00000000..c8094dca --- /dev/null +++ b/public/llms-content/dotkernel-api/error-reporting-endpoint-in-dotkernel-api.md @@ -0,0 +1,160 @@ +--- +title: "Error reporting endpoint in Dotkernel API" +description: "How the Dotkernel API error reporting endpoint lets frontend applications securely report bugs and data errors back to the API, including server-side and frontend setup." +author: "Florin Bidirean" +date_published: "2024-08-29" +canonical_url: "https://www.dotkernel.com/dotkernel-api/error-reporting-endpoint-in-dotkernel-api/" +category: "Dotkernel API" +language: "en" +--- + +# Error reporting endpoint in Dotkernel API + +## TL;DR + +Dotkernel API includes an error reporting endpoint that lets frontend developers securely report bugs and incorrect data processing back to the API, even when no fatal error shows up in the logs. +It works by sending a POST request to `/error-report` with a token in the header; the API validates the request against configured tokens, domains, and IPs before logging the message. +Setup involves generating a token, adding it to `config/autoload/error-handling.global.php`, and having the frontend send the `Error-Reporting-Token` and `Origin` headers. + +Dotkernel API has received a lot of love from our developers, with regular updates to the platform for years. +We use Dotkernel API in our projects, so any bugs and issues are addressed as soon as they are found. +Still, it's not unlikely that some hidden issues remain in fringe use cases that we simply haven't explored. +The occurrence of bugs increases when the API is used in a complex frontend project. + +Fatal errors are easily found in the API logs, but it's another matter altogether to deal with incorrect data processing that doesn't generate errors in the frontend that interfaces with the API. +The error reporting endpoint was designed to allow the frontend developers of your API to report any bugs they encounter in a secure way that is fully under your control. + +## Example Case Usage + +- Frontend developed in Angular. +- Frontend developer will use try-catch in the code in order to send frontend errors back to the API. + +## How to Use It on the API Side + +Error reporting is done by sending a POST request to the `/error-report` endpoint, together with a token in the header. +In the sections below we will detail how to configure error reporting in your API and how the endpoint is used by the frontend developers. + +### Generating a Token and Adding It to Your API Config + +First you need to generate a token for your request. +This is done by using the below command. + +```bash +php ./bin/cli.php token:generate error-reporting +``` + +The resulting token has this format `0123456789abcdef0123456789abcdef01234567`. +Note: this example is provided just to let you know what to look for. + +Copy the generated token in your `config/autoload/error-handling.global.php` file. +It should look similar to the example below. +Your API can have multiple tokens, if needed. + +```php +return , + ... + ] +] +``` + +### Validation Mechanism + +Behind the scenes, the API validates your configuration and lets you know if any config items prevent the submission of the error report. +Below are the requirements for an application to be able to send error messages to Dotkernel API. + +- Server-side requirements stored in `config/autoload/error-handling.global.php` (these can be set/overwritten in `config/autoload/local.php`): + - All keys (`enabled`, `path`, `tokens`, `domain_whitelist` and `ip_whitelist`) must exist under `ErrorReportServiceInterface::class`. + - The error reporting feature must be enabled by setting `ErrorReportServiceInterface::class` . `enabled` to `true`. + - `ErrorReportServiceInterface::class` . `path` must have a value; if the destination file does not exist, it will be created automatically. + - `ErrorReportServiceInterface::class` . `tokens` must contain at least one token. + - At least one of `ErrorReportServiceInterface::class` . `domain_whitelist`/`ip_whitelist` must have at least one value. + +Note: In `src/App/src/Service/ErrorReportService.php`, the method `checkRequest()` tries to validate the request by checking matches for `domain_whitelist` with `isMatchingDomain()` and for `ip_whitelist` with `isMatchingIpAddress()`. +If both return `false`, a `ForbiddenException` is thrown and the error message does not get stored. + +- Application-side requirements: + - Send the `Error-Reporting-Token` header with a valid token previously stored in `config/autoload/error-handling.global.php` in the `ErrorReportServiceInterface::class` . `tokens` array. + - Send the `Origin` header set to the application's URL; this is the application that sends the error message. + +Note: + +- The tokens under `ErrorReportServiceInterface::class` . `tokens` do not expire. +- The log file stores the token value too, making it easy to identify which application sent the error message. + +If your request passes all the checks, the message is saved in the log file specified in `ErrorReportServiceInterface::class` . `path`. + +### Tips and Tricks + +If there are multiple applications that report errors to your API, you can assign a different error reporting token for each. +The tokens support key-value pairs where: + +- The key is an alias relevant to the assigned application that uses it. +- The value is the token itself. + +Example: + +```php +// ... +return , + ], +]; +``` + +The log file will have entries similar to the below: + +> Demo error message + +The inclusion of the token helps you identify the source of the error message. +In our example, it's the application that uses the `0123456789abcdef0123456789abcdef01234567` token, which is assigned to the application `frontend`. + +## How to Use It on the Frontend Side (Angular Example) + +The API developer sends a generated token to the frontend developer who will save it in their `environment.staging.ts` and/or `environment.prod.ts`. +From then on, it's the frontend developer's job to set up an error reporting function similar to the one below. + +```typescript +postError(body: object): Promise { + return new Promise((resolve, reject) => { + return this.http.post(API_ENDPOINT + 'error-report', body , {headers: new HttpHeaders({'Error-Reporting-Token': 'TOKEN', 'Origin': 'https://example.com'})})).subscribe({ + next: (response: any) => { + resolve(response); + }, + error: (e: HttpErrorResponse) => reject(e), + complete: () => console.info('Error on sending error'), + }); + }); + } +``` + +Whenever an error is found, the frontend will call `postError()` with a relevant description under `message`. + +```typescript +apiService.postError({message: 'ERROR MESSAGE'}) +``` + +## Conclusion + +The error reporting feature in Dotkernel API is a secured and highly configurable tool for users of your API to report any unwanted behavior. +More often than not, a detailed error report will help developers understand how to replicate the issue and fix it in due course. + +This article is also included in the full API documentation [https://docs.dotkernel.org/api-documentation/v5/core-features/error-reporting](https://docs.dotkernel.org/api-documentation/v5/core-features/error-reporting). + +## FAQ + +**Q: What is the error reporting endpoint for?** +A: It lets frontend developers of an API report bugs and incorrect data processing back to the API in a secure, controlled way, which is especially useful for issues that don't show up as fatal errors in the API logs. + +**Q: How do you generate a token for error reporting?** +A: Run `php ./bin/cli.php token:generate error-reporting`, then copy the resulting token into `config/autoload/error-handling.global.php`. + +**Q: What server-side requirements must be met for error reporting to work?** +A: All required keys (`enabled`, `path`, `tokens`, `domain_whitelist`, `ip_whitelist`) must exist under `ErrorReportServiceInterface::class`, the feature must be enabled, `path` must have a value, `tokens` must contain at least one token, and at least one of `domain_whitelist`/`ip_whitelist` must have a value. + +**Q: What headers must the frontend application send?** +A: The `Error-Reporting-Token` header with a valid stored token, and the `Origin` header set to the application's URL. + +**Q: What happens if a request fails validation?** +A: The `checkRequest()` method checks the domain against `domain_whitelist` and the IP against `ip_whitelist`; if both checks fail, a `ForbiddenException` is thrown and the error message is not stored. + +**Q: How is the error reporting endpoint called?** +A: By sending a POST request to the `/error-report` endpoint along with a valid token in the header. diff --git a/public/llms-content/dotkernel-api/how-to-implement-mailchimp-in-dotkernel-api.md b/public/llms-content/dotkernel-api/how-to-implement-mailchimp-in-dotkernel-api.md new file mode 100644 index 00000000..84c35503 --- /dev/null +++ b/public/llms-content/dotkernel-api/how-to-implement-mailchimp-in-dotkernel-api.md @@ -0,0 +1,103 @@ +--- +title: "How to implement MailChimp in DotKernel API" +description: "A step-by-step guide to integrating MailChimp into a DotKernel API instance using the drewm/mailchimp-api library, from installation to wiring up a factory in the ConfigProvider." +author: "Alex Karajos" +date_published: "2020-01-04" +canonical_url: "https://www.dotkernel.com/dotkernel-api/how-to-implement-mailchimp-in-dotkernel-api/" +category: "Dotkernel API" +language: "en" +--- + +# How to implement MailChimp in DotKernel API + +## TL;DR + +This is a step-by-step guide to adding MailChimp support to a DotKernel API instance using the `drewm/mailchimp-api` library. +It covers installing the library, creating a MailChimp config file, building a factory that returns a `DrewM\MailChimp\MailChimp` instance, and registering that factory in `ConfigProvider.php` so it can be injected wherever needed. + +This article will walk you through the process of implementing MailChimp into your instance of [DotKernel API](https://github.com/dotkernel/api) using [drewm/mailchimp-api](https://github.com/drewm/mailchimp-api). + +Step 1: Add the library to your application using the following command: + +```bash +composer require drewm/mailchimp-api +``` + +Step 2: Create configuration file `config/autoload/mailchimp.global.php` and paste the following content inside of it: + +```php +get('config') ?? []; + + return new MailChimp($config ?? ''); + } +} +``` + +Step 4: Let your application use this factory by adding it to the main ConfigProvider. +To do this, open file `src/App/src/ConfigProvider.php` and locate the method called `getDependencies()`. +Inside this method, locate the key `factories` which points to an array. +Inside this array add the following line: + +```php +MailChimp::class => MailChimpFactory::class, +``` + +Make sure you add the corresponding uses: + +```php +use Api\App\MailChimp\Factory\MailChimpFactory; +use DrewM\MailChimp\MailChimp; +``` + +After this, you can start using the library by @Injecting `MailChimp::class` where it's needed. + +## FAQ + +**Q: Which library does this tutorial use to add MailChimp to Dotkernel API?** +A: The tutorial uses drewm/mailchimp-api, installed with the command composer require drewm/mailchimp-api. + +**Q: Where do you place the MailChimp configuration file?** +A: In config/autoload/mailchimp.global.php, a new configuration file created as part of Step 2. + +**Q: What does the MailChimpFactory class do?** +A: It's a factory, created at src/App/src/MailChimp/Factory/MailChimpFactory.php, that reads the config from the container and returns an instance of DrewM\MailChimp\MailChimp. + +**Q: Where do you register the MailChimp factory so the application can use it?** +A: In src/App/src/ConfigProvider.php, inside the getDependencies() method's factories array, by mapping MailChimp::class to MailChimpFactory::class, plus adding the corresponding use statements for MailChimp and MailChimpFactory. + +**Q: How do you use MailChimp once it's wired up?** +A: By injecting MailChimp::class wherever it's needed, using @Inject. diff --git a/public/llms-content/dotkernel-api/openapi-implementation-in-dotkernel-api.md b/public/llms-content/dotkernel-api/openapi-implementation-in-dotkernel-api.md new file mode 100644 index 00000000..98dc471a --- /dev/null +++ b/public/llms-content/dotkernel-api/openapi-implementation-in-dotkernel-api.md @@ -0,0 +1,141 @@ +--- +title: "OpenAPI implementation in Dotkernel API" +description: "An overview of the OpenAPI Specification, why it complements tools like Postman, and how Dotkernel API implements OpenAPI documentation across its modules." +author: "Florin Bidirean" +date_published: "2024-07-30" +canonical_url: "https://www.dotkernel.com/dotkernel-api/openapi-implementation-in-dotkernel-api/" +category: "Dotkernel API" +language: "en" +--- + +# OpenAPI implementation in Dotkernel API + +## TL;DR + +OpenAPI is a specification for describing an API's structure in a language-agnostic, machine-readable way, offering benefits like standardization, automatic documentation, upfront design, and better collaboration compared to a tool like Postman. +Dotkernel API has full OpenAPI support: each module (Admin, App, User) documents its endpoints in an `OpenAPI.php` file, which `zircote/swagger-php` turns into documentation rendered via Swagger UI or Redoc. +Testing protected endpoints in Swagger UI requires generating an authentication token that matches the endpoint's required privileges. + +## What Is OpenAPI? + +The OpenAPI Specification provides a consistent way to develop and interact with an API. +It defines API structure and syntax in a universal way, regardless of the programming language used in the API's development. +API specifications typically use YAML or JSON to share and use the specification. +They allow users of the API to quickly discover how it works by describing its elements, e.g. endpoints, request and response formats, security mechanisms and more. + +While not mutually exclusive, OpenAPI has several benefits over Postman: + +- API standardization: this offers a standard way to describe and document endpoints, request/response models, and other details of your API that enforces design best practices. +Postman has no focus on this topic. +- Automatic generation of API documentation: create comprehensive, machine-readable documentation that helps developers understand how to interact with your API. +Postman is not designed to explain the API's components. +- API design and development: define your API specification, most commonly using YAML and JSON formats, before starting development. +Postman is used only to test an existing, completed endpoint. +- Improved collaboration: this benefits frontend and backend developers, as well as operations teams. +Postman's free tier is aimed more towards individual or small team development. +- API gateways and management: a wide range of tools and platforms that support OpenAPI allow more streamlined monitoring and management of APIs. +Postman has environment management, but primarily on the developer's machine. + +Other benefits from using OpenAPI: + +- Code generation: automatically generate client code, server stubs, API documentation and even test cases to ensure consistency between the API documentation and implementation. +- Interoperability: standardization using OpenAPI ensures that the API can interface with other systems. +- Testing and validation: the specification can generate tests to catch bugs early on and ensure correct functionality. +- Versioning and change management: keeps track of changes and ensures backward compatibility. + +## The Importance of API Documentation + +API documentation, in general, is crucial for several reasons. +It serves multiple stakeholders that use the API for development, integration and maintenance. + +- Faster developer onboarding, adoption and integration: helps developers understand the API better and reduces the learning curve for adopting and integrating the API into other systems. +The API documentation should be publicly available, especially if the API is public. +It's even more beneficial if the documentation is integrated with a developer portal. +- Better collaboration: promotes consistency and reduces misunderstandings between developers and users. +- Better API quality and maintenance: includes details on how to properly use the API, from its data types and required parameters, to error handling procedures. +This helps maintain existing functionality when changes are implemented. +- Helps troubleshooting: it defines the correct functionality that helps developers and maintainers find and fix bugs more effectively. + +## OpenAPI in DotKernel API + +DotKernel API has full support for OpenAPI, from describing the endpoints and generating the documentation, to rendering and testing the endpoints. + +Each module (Admin, App, User) in DotKernel API contains a file named `OpenAPI.php`. +In this file you must document all of the endpoints from `RoutesDelegator.php`. +The entries in `OpenAPI.php` have several descriptive items, the most important being method, request and response. +These are used to generate a documentation file from the command line. +The static documentation file is rendered using Swagger UI or Redoc in a user-friendly way. +You can read more about this [starting here](https://docs.dotkernel.org/api-documentation/v5/openapi/introduction/) and its subsequent pages. + +### Describing OpenAPI Components + +All OpenAPI components require a handful of components that are universally valid for a given project. +These are below: + +- OA\Info contains basic information on your project, like version and name. +- OA\Server has one or more urls to a target host. +- OA\SecurityScheme describes the protection for the endpoint. +- OA\ExternalDocumentation has a url and description for extended documentation related to an item. +- OA\Schema describes a object (e.g. entity) or collection of objects in your project. + +Read more details about the above [here](https://docs.dotkernel.org/api-documentation/v5/openapi/initialized-components/). + +Once you have your basic components defined, you can begin work on the endpoints. +The endpoints already made available in DotKernel API are documented, so you must do the same for the new endpoints you create in your project. +This is done by defining these items: + +- the request object (Get, Post, Patch, Put, Delete) +- the path to the resource +- the endpoint's summary and description +- the query/path parameters, if required +- the request body, if required +- the security scheme, if required +- the response + +Wherever it's appropriate, schemas should be used to ensure consistency. +The optional 'tags' item can be used to group operations together. +Read more [here](https://docs.dotkernel.org/api-documentation/v5/openapi/initialized-components/). + +### Generating the Documentation + +The documentation is generated using [zircote/swagger-php](https://github.com/zircote/swagger-php). +It uses the descriptions you added in the `OpenAPI.php` files to build the documentation file. +The documentation contents can be listed in the command line or saved to a file in yaml of json format. +You can read more [here](https://docs.dotkernel.org/api-documentation/v5/openapi/generate-documentation/). + +### Alternatives for Rendering the Documentation + +Once you have the documentation generated, it can be rendered in two ways: + +- Swagger UI allows you to visualize and interact with the API's resources without worrying about the implementation logic. +- Redoc lists the documentation in read-only mode, detailing example requests and responses. + +### Handling Authentication for Swagger UI + +Most endpoints for your API should be protected, so to access them you are required to generate an authentication token (AuthToken). +The token is related to the user type, so make sure to check the privileges required for the endpoint you are testing. +After you submit the token, you can test the endpoints as an authenticated user. +Clicking on the 'Try it out' button will activate the required parameter input fields and the textarea for the request body. +The 'Execute' button will send the request and return the response, along with its HTTP status code. +You can read more details [here](https://docs.dotkernel.org/api-documentation/v5/openapi/use-documentation/). + +## FAQ + +**Q: What is the OpenAPI Specification?** +A: A consistent way to develop and interact with an API. It defines API structure and syntax in a universal way, regardless of the programming language used, typically described in YAML or JSON so users can quickly discover endpoints, request/response formats, security mechanisms and more. + +**Q: How does OpenAPI compare to Postman?** +A: OpenAPI standardizes how endpoints and request/response models are described, automatically generates machine-readable documentation, lets you define the API specification before development starts, and improves collaboration across teams. +Postman, by contrast, is used mainly to test an already-completed endpoint and has no real focus on standardized documentation or upfront design. + +**Q: Where do you document endpoints in Dotkernel API?** +A: Each module (Admin, App, User) contains an OpenAPI.php file, where all endpoints from that module's RoutesDelegator.php must be documented, primarily describing the method, request and response. + +**Q: What core components does every OpenAPI description need?** +A: OA\Info (basic project info like version and name), OA\Server (one or more target host URLs), OA\SecurityScheme (endpoint protection), OA\ExternalDocumentation (link and description for extended docs), and OA\Schema (describing an object or collection of objects). + +**Q: What generates the documentation file from the OpenAPI.php descriptions?** +A: zircote/swagger-php, which uses the descriptions added in the OpenAPI.php files to build the documentation. The result can be listed in the command line or saved to a file in YAML or JSON format. + +**Q: How is the generated documentation rendered, and how do you test protected endpoints?** +A: It can be rendered with Swagger UI, which lets you visualize and interact with the API's resources, or with Redoc, which lists the documentation in read-only mode. Because most endpoints are protected, testing them in Swagger UI requires generating an authentication token (AuthToken) matching the required privileges, then using the 'Try it out' button to fill in parameters/body and 'Execute' to send the request and see the response with its HTTP status code. diff --git a/public/readme/dotkernel/adding-a-cors-implementation-to-zend-expressive.md b/public/llms-content/dotkernel/adding-a-cors-implementation-to-zend-expressive.md similarity index 80% rename from public/readme/dotkernel/adding-a-cors-implementation-to-zend-expressive.md rename to public/llms-content/dotkernel/adding-a-cors-implementation-to-zend-expressive.md index 8edb02a4..b076b16f 100644 --- a/public/readme/dotkernel/adding-a-cors-implementation-to-zend-expressive.md +++ b/public/llms-content/dotkernel/adding-a-cors-implementation-to-zend-expressive.md @@ -11,19 +11,23 @@ language: "en" # Adding a CORS implementation to Zend Expressive ## TL;DR -When a client-side request is blocked with a "No 'Access-Control-Allow-Origin' header" error, it's because the server isn't sending the header that allows a browser to access its data (most common when fetching JSON to process with JavaScript). This guide adds CORS support to a Zend Expressive / DotKernel3 project using Tuupola's Cors Middleware package. +When a client-side request is blocked with a "No 'Access-Control-Allow-Origin' header" error, it's because the server isn't sending the header that allows a browser to access its data (most common when fetching JSON to process with JavaScript). +This guide adds CORS support to a Zend Expressive / DotKernel3 project using Tuupola's Cors Middleware package. ## The issue If you're facing the error: -> "Access to XMLHttpRequest at 'url' has been blocked by cors policy. No 'Access-Control-Allow-Origin header is present on the requested resource." +> "Access to XMLHttpRequest at 'url' has been blocked by cors policy. +> No 'Access-Control-Allow-Origin header is present on the requested resource." -it means the server didn't send the header that lets you access its data through a local client (e.g. a browser). This issue is most common when trying to get data (usually JSON) that you want to process using JavaScript. +it means the server didn't send the header that lets you access its data through a local client (e.g. a browser). +This issue is most common when trying to get data (usually JSON) that you want to process using JavaScript. ## The solution -A simple implementation uses [Tuupola's Cors Middleware](https://packagist.org/packages/tuupola/cors-middleware) package. (This article was inspired by [akrabat.com/implementing-tuupola-cors-in-expressive](https://akrabat.com/implementing-tuupola-cors-in-expressive/).) +A simple implementation uses [Tuupola's Cors Middleware](https://packagist.org/packages/tuupola/cors-middleware) package. +(This article was inspired by [akrabat.com/implementing-tuupola-cors-in-expressive](https://akrabat.com/implementing-tuupola-cors-in-expressive/).) ### 1. Add the package to your project @@ -114,12 +118,14 @@ return function (Application $app, MiddlewareFactory $factory, ContainerInterfac }; ``` -Add the CORS middleware **after** the Error handler and **before** the middleware providing the data you want to access, to make sure everything runs smoothly. This should get your project working with CORS. +Add the CORS middleware **after** the Error handler and **before** the middleware providing the data you want to access, to make sure everything runs smoothly. +This should get your project working with CORS. ## FAQ **Q: What causes the "No 'Access-Control-Allow-Origin' header" error?** -A: It means the server didn't send the header that lets a local client, such as a browser, access its data. This is most common when trying to fetch data (usually JSON) that you want to process using JavaScript. +A: It means the server didn't send the header that lets a local client, such as a browser, access its data. +This is most common when trying to fetch data (usually JSON) that you want to process using JavaScript. **Q: What package does the article use to add CORS support?** A: Tuupola's Cors Middleware package, installed by running `composer require tuupola/cors-middleware` in the project. @@ -128,7 +134,8 @@ A: Tuupola's Cors Middleware package, installed by running `composer require tuu A: In a `cors.global.php` file created in the config/autoload directory, containing a "cors" key with settings like origin, methods, headers.allow, headers.expose, credentials, and cache. **Q: How is the CorsMiddleware wired into the container?** -A: A CorsMiddlewareFactory extracts the "cors" config array (or an empty array if it's not provided) and instantiates Tuupola's CorsMiddleware with it. That factory is registered under the "dependencies" > "factories" section of cors.global.php. +A: A CorsMiddlewareFactory extracts the "cors" config array (or an empty array if it's not provided) and instantiates Tuupola's CorsMiddleware with it. +That factory is registered under the "dependencies" > "factories" section of cors.global.php. **Q: Where should the CORS middleware be added in the pipeline?** A: In config/pipelines.php via `$app->pipe(CorsMiddleware::class)`, placed after the Error handler and before the middleware that provides the data you want to access. diff --git a/public/readme/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md b/public/llms-content/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md similarity index 82% rename from public/readme/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md rename to public/llms-content/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md index 8b442f9d..e0e8c7f1 100644 --- a/public/readme/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md +++ b/public/llms-content/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md @@ -15,7 +15,9 @@ On a high-traffic project using WURFL, profiling showed WURFL's default filesyst ## The problem -On one recent project that used WURFL, response time was an important factor. Profiling revealed that the greatest chunk of response time (up to a few hundred milliseconds) was taken up by WURFL. The default filesystem cache turned out to be too slow for a relatively high-traffic application. +On one recent project that used WURFL, response time was an important factor. +Profiling revealed that the greatest chunk of response time (up to a few hundred milliseconds) was taken up by WURFL. +The default filesystem cache turned out to be too slow for a relatively high-traffic application. ## How WURFL's caching works @@ -41,13 +43,16 @@ This small change (under 10 lines of code) decreased response time by an **order A: Profiling revealed that WURFL's default filesystem cache was taking up to a few hundred milliseconds of response time, which was too slow for a relatively high-traffic application. **Q: How does WURFL's caching work by default?** -A: Device data is stored in a large zipped XML file. On first use, WURFL unzips the file, serializes each device's data, and writes it to cache using an MD5 signature of the user agent as the key. Because devices are stored as a tree inheriting properties from parent nodes, each lookup requires reading and merging several files. +A: Device data is stored in a large zipped XML file. +On first use, WURFL unzips the file, serializes each device's data, and writes it to cache using an MD5 signature of the user agent as the key. +Because devices are stored as a tree inheriting properties from parent nodes, each lookup requires reading and merging several files. **Q: Did WURFL's built-in APC or memcache cache providers solve the problem?** A: No. The team tried WURFL's existing cache providers for APC and memcache, but the results weren't impressive. **Q: What was the actual solution?** -A: Adding a second cache layer on top of WURFL's own cache, using APC and storing arrays of only the specific fields they actually needed in User Cache Entries. The change was under 10 lines of code. +A: Adding a second cache layer on top of WURFL's own cache, using APC and storing arrays of only the specific fields they actually needed in User Cache Entries. +The change was under 10 lines of code. **Q: What performance improvement did this bring?** A: Response time decreased by an order of magnitude, down to about 20-30ms. diff --git a/public/readme/dotkernel/adding-composer-support-in-your-dotkernel-project.md b/public/llms-content/dotkernel/adding-composer-support-in-your-dotkernel-project.md similarity index 84% rename from public/readme/dotkernel/adding-composer-support-in-your-dotkernel-project.md rename to public/llms-content/dotkernel/adding-composer-support-in-your-dotkernel-project.md index 3bbc463c..69892e05 100644 --- a/public/readme/dotkernel/adding-composer-support-in-your-dotkernel-project.md +++ b/public/llms-content/dotkernel/adding-composer-support-in-your-dotkernel-project.md @@ -11,11 +11,13 @@ language: "en" # Adding Composer support in your DotKernel project ## TL;DR -Composer is an application-level package manager that auto-loads dependencies (and custom classes) on demand. This article covers the steps needed to add a composer.json file to a DotKernel project, run `composer update`, and safely require the generated autoloader so the project works whether or not Composer is present. +Composer is an application-level package manager that auto-loads dependencies (and custom classes) on demand. +This article covers the steps needed to add a composer.json file to a DotKernel project, run `composer update`, and safely require the generated autoloader so the project works whether or not Composer is present. ## First things first -The DotKernel project must have a **composer.json** file so that Composer can work. It should look like this: +The DotKernel project must have a **composer.json** file so that Composer can work. +It should look like this: ```json { @@ -56,7 +58,8 @@ $composerAutoLoaderPath = realpath(APPLICATION_PATH.'/vendor/autoload.php'); require_once($composerAutoLoaderPath); ``` -But what if the file does not exist, or Composer is not present? First make sure the Composer autoload path exists, and only load the dependencies if the autoload file was found: +But what if the file does not exist, or Composer is not present? +First make sure the Composer autoload path exists, and only load the dependencies if the autoload file was found: ```php $composerAutoLoaderPath = realpath('./vendor/autoload.php'); @@ -87,13 +90,15 @@ This article works for any **DotKernel 1.x** version if your server is running * ## FAQ **Q: What does Composer do?** -A: Composer is an application-level package manager. It auto-loads dependencies on demand and can also auto-load custom classes. +A: Composer is an application-level package manager. +It auto-loads dependencies on demand and can also auto-load custom classes. **Q: What must a DotKernel project have before Composer can be used?** A: A composer.json file, for example requiring zendframework/zendframework1 at 1.12.* and mobiledetect/mobiledetectlib at 2.8.*, plus PHP >=5.4.0 listed under require-dev. **Q: What happens when you run composer update?** -A: If the vendor folder already exists, Composer checks for and applies updates to the packages. If it doesn't exist, Composer creates the vendor folder containing all requested packages, along with an autoload file. +A: If the vendor folder already exists, Composer checks for and applies updates to the packages. +If it doesn't exist, Composer creates the vendor folder containing all requested packages, along with an autoload file. **Q: How do you safely load the Composer autoloader in case Composer isn't present?** A: Check whether vendor/autoload.php exists using file_exists() before calling require_once() on it, and handle the case gracefully (for example by loading fallbacks) if the path is missing. diff --git a/public/readme/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md b/public/llms-content/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md similarity index 95% rename from public/readme/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md rename to public/llms-content/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md index 72a3dd02..429e10ec 100644 --- a/public/readme/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md +++ b/public/llms-content/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md @@ -11,7 +11,8 @@ language: "en" # Adding Windows 10 OS and Browser detection in DotKernel projects ## TL;DR -DotKernel added Windows 8, 8.1 and 10 OS icons and a Microsoft Edge browser icon, shown in the User and Admin login icons. This article is the upgrade guide for applying that icon patch. +DotKernel added Windows 8, 8.1 and 10 OS icons and a Microsoft Edge browser icon, shown in the User and Admin login icons. +This article is the upgrade guide for applying that icon patch. ## Upgrade steps diff --git a/public/readme/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md b/public/llms-content/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md similarity index 96% rename from public/readme/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md rename to public/llms-content/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md index 9993463e..64257d76 100644 --- a/public/readme/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md +++ b/public/llms-content/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md @@ -11,7 +11,8 @@ language: "en" # Autologin using Cookie / Remember Me in Dotkernel ## TL;DR -This feature automatically logs in a user who checks the "remember me" box at login. It has been implemented in [Dotkernel Frontend](https://github.com/dotkernel/frontend) starting from Release 3.3.0, and requires changes across the login form, a new entity/migration, a new middleware, config, and the user service/repository/controller. +This feature automatically logs in a user who checks the "remember me" box at login. +It has been implemented in [Dotkernel Frontend](https://github.com/dotkernel/frontend) starting from Release 3.3.0, and requires changes across the login form, a new entity/migration, a new middleware, config, and the user service/repository/controller. ## Add remember me button to user interface diff --git a/public/readme/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md b/public/llms-content/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md similarity index 94% rename from public/readme/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md rename to public/llms-content/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md index 1bf6c3b2..f93fa85d 100644 --- a/public/readme/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md +++ b/public/llms-content/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md @@ -10,7 +10,8 @@ language: "en" # Avoid routing through bootstrap of non existent files -In some cases you may encounter missing files: images, CSS, or JS files. All those missing files are processed by the current bootstrap: `index.php`. +In some cases you may encounter missing files: images, CSS, or JS files. +All those missing files are processed by the current bootstrap: `index.php`. If the session is set to regenerate on each request, as a normal security measure, the currently logged-in user is logged off, because the session ID is different now. diff --git a/public/readme/dotkernel/caching-in-dotkernel-using-zend-framework.md b/public/llms-content/dotkernel/caching-in-dotkernel-using-zend-framework.md similarity index 75% rename from public/readme/dotkernel/caching-in-dotkernel-using-zend-framework.md rename to public/llms-content/dotkernel/caching-in-dotkernel-using-zend-framework.md index a55c42e1..6b245b5b 100644 --- a/public/readme/dotkernel/caching-in-dotkernel-using-zend-framework.md +++ b/public/llms-content/dotkernel/caching-in-dotkernel-using-zend-framework.md @@ -11,15 +11,19 @@ language: "en" # Caching in DotKernel using Zend Framework ## TL;DR -Loading configuration and settings from XML files on every request is expensive, both due to hard-drive latency and XML parsing overhead. DotKernel 1.8 implements a cache layer for router, acl_role, menu, options (including seo_xml), browser_xml, os_xml and test data, with a choice of APC/APCU or file-based storage. +Loading configuration and settings from XML files on every request is expensive, both due to hard-drive latency and XML parsing overhead. +DotKernel 1.8 implements a cache layer for router, acl_role, menu, options (including seo_xml), browser_xml, os_xml and test data, with a choice of APC/APCU or file-based storage. ## 1. Configuring the cache -The configuration is set from `/configs/application.ini`: whether caching is enabled, how long the cache stays valid, the cache namespace, and the storage provider (File or APC). The article recommends disabling the cache in development mode. See [Configuring the Cache in DotKernel](http://www.dotkernel.com/dotkernel/configuring-the-cache-in-dotkernel/) for more details. +The configuration is set from `/configs/application.ini`: whether caching is enabled, how long the cache stays valid, the cache namespace, and the storage provider (File or APC). +The article recommends disabling the cache in development mode. +See [Configuring the Cache in DotKernel](http://www.dotkernel.com/dotkernel/configuring-the-cache-in-dotkernel/) for more details. ## 2. Using the cache -The cache is automatically loaded during initialization and stored in the Registry — loading it manually is not needed because it's already loaded on kernel initialization (see `Dot_Kernel::initialize($startTime)`). If you want to use caching outside of that normal initialization, load it with: +The cache is automatically loaded during initialization and stored in the Registry — loading it manually is not needed because it's already loaded on kernel initialization (see `Dot_Kernel::initialize($startTime)`). +If you want to use caching outside of that normal initialization, load it with: ```php Dot_Cache::loadCache(); @@ -58,10 +62,12 @@ A: Router, acl_role, menu, options (including seo_xml), browser_xml, os_xml, and A: Two cache factories to choose from: APC (or APCU for newer PHP installations) and File. **Q: Where is the cache configured?** -A: In /configs/application.ini, where you can enable or disable caching, set how long the cache stays valid, choose the cache namespace, and pick the storage provider (File or APC). The article recommends disabling the cache in development mode. +A: In /configs/application.ini, where you can enable or disable caching, set how long the cache stays valid, choose the cache namespace, and pick the storage provider (File or APC). +The article recommends disabling the cache in development mode. **Q: Do you need to manually load the cache engine?** -A: No, it's automatically loaded during kernel initialization (Dot_Kernel::initialize()). Manually calling Dot_Cache::loadCache() is only needed if you want to use caching outside of that normal initialization. +A: No, it's automatically loaded during kernel initialization (Dot_Kernel::initialize()). +Manually calling Dot_Cache::loadCache() is only needed if you want to use caching outside of that normal initialization. **Q: Can you cache PHP objects, not just simple values?** A: Yes, the article shows an example of saving and loading a stdClass object using Dot_Cache::save() and Dot_Cache::load(). diff --git a/public/readme/dotkernel/camelcase-table-names-in-mysql-on-windows.md b/public/llms-content/dotkernel/camelcase-table-names-in-mysql-on-windows.md similarity index 100% rename from public/readme/dotkernel/camelcase-table-names-in-mysql-on-windows.md rename to public/llms-content/dotkernel/camelcase-table-names-in-mysql-on-windows.md diff --git a/public/readme/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md b/public/llms-content/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md similarity index 86% rename from public/readme/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md rename to public/llms-content/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md index 559bdab4..2b43175d 100644 --- a/public/readme/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md +++ b/public/llms-content/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md @@ -10,7 +10,9 @@ language: "en" # Commitment to PHP - new Zend Certified Engineers - ZCE - in our team -Another 2 of our team members passed the ZCE exam. Now we are 5. That means we are really taking PHP into serious consideration, and at the very least we have good technical skills. +Another 2 of our team members passed the ZCE exam. +Now we are 5. +That means we are really taking PHP into serious consideration, and at the very least we have good technical skills. See the [Zend Yellow Pages](http://www.zend.com/store/education/certification/yellow-pages.php#list-cid=0&sid=&certtype_zf=1&certtype_php=1&certtype=&firstname=&lastname=&company=Dotboost%20Technologies&ClientCandidateID=). diff --git a/public/llms-content/dotkernel/configuring-the-cache-in-dotkernel.md b/public/llms-content/dotkernel/configuring-the-cache-in-dotkernel.md new file mode 100644 index 00000000..e77dae77 --- /dev/null +++ b/public/llms-content/dotkernel/configuring-the-cache-in-dotkernel.md @@ -0,0 +1,71 @@ +--- +title: "Configuring the Cache in DotKernel" +description: "A configuration guide for DotKernel's Zend Framework Cache-based caching layer, covering the main frontend settings and the optional per-backend settings." +author: "Gabi DJ" +date_published: "2015-01-29" +canonical_url: "https://www.dotkernel.com/dotkernel/configuring-the-cache-in-dotkernel/" +category: "Dotkernel" +language: "en" +--- + +# Configuring the Cache in DotKernel + +## TL;DR + +DotKernel's caching layer is built on Zend Framework Cache and is configured through `cache.*` settings in `application.ini`. +The main frontend settings control whether caching is enabled, which cache service to use, the namespace prefix, and how long entries live. +Optional backend-specific settings (like the file cache directory) are recommended so that separate projects don't accidentally share the same cache. + +This article contains the DotKernel cache layer configuration guide. +The DotKernel Caching Layer is based on Zend Framework Cache; more configuration options can be found at the following links: + +- [Zend Framework Cache Frontends](http://framework.zend.com/manual/1.12/en/zend.cache.frontends.html) +- [Zend Framework Cache Backends](http://framework.zend.com/manual/1.12/en/zend.cache.backends.html) + +## Main Cache Settings (Cache Frontend) + +The main cache settings within the application.ini file should look like this: + +```ini +cache.enable = true +cache.factory = "apc" +cache.lifetime = "86400" +cache.namespace = "dotkernel" +``` + +The cache.enable option can be used to disable caching, mostly used in the development stage. +The cache.factory value will be the cache service we want to use: file or apc. +The cache.namespace will be the cache variables prefix, and the cache.lifetime value will define how long the cached variables will be usable before they need to be re-cached. + +## Individual Cache Settings (Cache Backend) + +The individual cache settings are optional, but it's highly recommended that you have these values set, otherwise other projects might use the same cache. + +```ini +; file caching settings +cache.file.cache_dir = APPLICATION_PATH "/cache" +cache.file.cache_file_perm = 0600 +``` + +For more settings and caching alternatives, see the Zend Framework Cache links at the beginning of the article. +The setting pattern and sample are below: + +```ini +cache.BACKEND_NAME.SETTING = "VALUE" +; example: +cache.file.file_name_prefix = "DotKernel" +``` + +## FAQ + +**Q: What is DotKernel's caching layer based on?** +A: It's based on Zend Framework Cache, configured through settings in application.ini, with more configuration options available at the Zend Framework Cache Frontends and Backends documentation links given in the article. + +**Q: What does the cache.enable setting do?** +A: It can be used to disable caching, which is mostly useful during the development stage. + +**Q: What values can cache.factory take?** +A: The cache.factory value is the cache service to use, and the article lists two options: file or apc. + +**Q: Why bother setting the individual/backend cache settings like cache.file.cache_dir?** +A: These settings are optional, but the article highly recommends setting them, otherwise other projects might end up using the same cache. diff --git a/public/llms-content/dotkernel/dependency-injection-made-easy-in-laminas-mezzio-applications.md b/public/llms-content/dotkernel/dependency-injection-made-easy-in-laminas-mezzio-applications.md new file mode 100644 index 00000000..f422c458 --- /dev/null +++ b/public/llms-content/dotkernel/dependency-injection-made-easy-in-laminas-mezzio-applications.md @@ -0,0 +1,178 @@ +--- +title: "Dependency Injection made easy in Laminas/Mezzio applications" +description: "Introduces DotKernel's dot-dependency-injection package, which autowires constructor dependencies in Laminas/Mezzio applications via a PHP attribute instead of a hand-written factory per class." +author: "Claudiu Pintiuta" +date_published: "2024-06-20" +canonical_url: "https://www.dotkernel.com/dotkernel/dependency-injection-made-easy-in-laminas-mezzio-applications/" +category: "Dotkernel" +language: "en" +--- + +# Dependency Injection made easy in Laminas/Mezzio applications + +## TL;DR + +DotKernel's dot-dependency-injection package autowires constructor dependencies in Laminas/Mezzio (and other PSR-11) applications, removing the need to write and maintain a custom factory class for every service. +Instead of a bespoke factory, you add an attribute to the class constructor and register a single shared AttributedServiceFactory in your ConfigProvider. +The package requires Doctrine ORM but can still be used in applications that don't integrate Doctrine, and it also supports injecting Doctrine repositories directly instead of fetching them from the EntityManager. + +> Note: The package requires Doctrine ORM. Still, it can be used in applications which do not integrate Doctrine. + +So, first thing first, the problem. +You have a Laminas / Mezzio application with a bunch of services that you need to use in a, let's say, controller class or in any other class, and you are tired of building, updating, and maintaining factories every time you add a new dependency to your class. + +DotKernel has you covered. +We built a tool to autowire those dependencies in your class. +There is no need for factories for every class you make. +Just use one "factory" class that you tie to your custom class in the config, and that's it. + +Sounds easy, right? +Let's finish with the chat and speak some code, first showing the problem and then the solution. + +> The examples below are from the [DotKernel API framework](https://github.com/dotkernel/api), but the pattern applies to all laminas and mezzio applications and to all PSR-11 applications. + +```php +class UserHandler implements RequestHandlerInterface +{ + public function __construct( + protected UserServiceInterface $userService, + protected array $config, + ) { + } +} +``` + +Above, we have a UserHandler (Controller), and we have the required dependencies: `UserService` and `config`. +Normally, we would build a factory for this to get things from the container and put them in the config provider like this: + +```php +class UserHandlerFactory +{ + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function __invoke(ContainerInterface $container) + { + $userService = $container->get(UserService::class); + assert($userService instanceof UserService); + + $config = $container->get('config'); + + return new UserHandler($userService, $config); + } +} +``` + +And in the config provider, we would have the following: + +```php +public function getDependencies(): array +{ + return + ]; +} +``` + +In one more example, let's look at the real-world required dependencies for `UserService`, the dependency that is required for `UserHandler`. + +```php +class UserService implements UserServiceInterface +{ + public function __construct( + protected UserRoleServiceInterface $userRoleService, + protected MailService $mailService, + protected TemplateRendererInterface $templateRenderer, + protected OAuthAccessTokenRepository $oAuthAccessTokenRepository, + protected OAuthRefreshTokenRepository $oAuthRefreshTokenRepository, + protected UserRepository $userRepository, + protected UserDetailRepository $userDetailRepository, + protected UserResetPasswordRepository $userResetPasswordRepository, + protected LoggerInterface $logger, + protected array $config = [], + ) { + } +} +``` + +Now consider that we need to build the factory for this and update it when we add a new dependency, and so on. +We'd also need to build the logic in the factory to handle any dependencies missing from the container. +Painful, right? + +Now let's use DotKernel's [dot-dependency-injection](https://github.com/dotkernel/dot-dependency-injection) package to inject the required dependency into your class. + +After you install the package, your class needs to `use Dot\DependencyInjection\Attribute\Inject`, then you need to add the `#` attribute to the constructor definition to specify which dependencies should be injected. + +```php +use Dot\DependencyInjection\Attribute\Inject; + +class UserHandler implements RequestHandlerInterface +{ + # + public function __construct( + protected UserServiceInterface $userService, + protected array $config, + ) { + } +} +``` + +Add the `Dot\DependencyInjection\Factory\AttributedServiceFactory` class to your `ConfigProvider`: + +```php +public function getDependencies(): array +{ + return + ]; +} +``` + +That's right, the `AttributedServiceFactory` class is the only one you need to add to your config, so you are ready to go. +This class will "build" the factory for you and will handle all the logic if any dependencies are not found in the container, with appropriate exceptions and messages. + +One more time, let's see how the `UserService` will look now. + +```php +class UserService implements UserServiceInterface +{ + use Dot\DependencyInjection\Attribute\Inject; + + # + public function __construct( + protected UserRoleServiceInterface $userRoleService, + protected MailService $mailService, + protected TemplateRendererInterface $templateRenderer, + protected OAuthAccessTokenRepository $oAuthAccessTokenRepository, + protected OAuthRefreshTokenRepository $oAuthRefreshTokenRepository, + protected UserRepository $userRepository, + protected UserDetailRepository $userDetailRepository, + protected UserResetPasswordRepository $userResetPasswordRepository, + protected LoggerInterface $logger, + protected array $config = [], + ) { + } + +} +``` + +## And, That's Not All. + +If you use Doctrine and the repository pattern and you don't want to get your repository from `EntityManager` and want to inject it into your service, this package covers that too. +The principle is the same, and for more insight about this, you can check the package documentation at [dot-dependency-injection](https://docs.dotkernel.org/dot-dependency-injection/). + +## FAQ + +**Q: What problem does dot-dependency-injection solve?** +A: In Laminas/Mezzio applications, developers normally have to build, update, and maintain a factory class for every class that needs dependencies. dot-dependency-injection autowires those dependencies instead, so you don't need a factory for every class. + +**Q: Does dot-dependency-injection require Doctrine ORM?** +A: The package requires Doctrine ORM, but the article notes it can still be used in applications that don't integrate Doctrine. + +**Q: How do you mark a class's constructor dependencies for injection?** +A: Import Dot\DependencyInjection\Attribute\Inject in the class, then add the attribute to the constructor definition to specify which dependencies should be injected. + +**Q: What do you need to add to the ConfigProvider to use this package?** +A: Only the Dot\DependencyInjection\Factory\AttributedServiceFactory class needs to be added to your config's dependencies. It builds the factory for you and handles the logic for dependencies missing from the container, with appropriate exceptions and messages. + +**Q: Can this package be used with the Doctrine repository pattern?** +A: Yes. If you don't want to fetch a repository from the EntityManager and instead want to inject it directly into your service, the article says this package covers that too, following the same principle, with more details in the package documentation. diff --git a/public/llms-content/dotkernel/detecting-mobile-devices-in-dotkernel-1-6-0.md b/public/llms-content/dotkernel/detecting-mobile-devices-in-dotkernel-1-6-0.md new file mode 100644 index 00000000..76d0bf03 --- /dev/null +++ b/public/llms-content/dotkernel/detecting-mobile-devices-in-dotkernel-1-6-0.md @@ -0,0 +1,116 @@ +--- +title: "Detecting Mobile Devices in DotKernel 1.6.0" +description: "Explains how mobile device detection changed in DotKernel 1.6.0 with the move to Wurfl Cloud, including the required application.ini settings and sample Dot_UserAgent usage code." +author: "deddu" +date_published: "2012-05-18" +canonical_url: "https://www.dotkernel.com/dotkernel/detecting-mobile-devices-in-dotkernel-1-6-0/" +category: "Dotkernel" +language: "en" +--- + +# Detecting Mobile Devices in DotKernel 1.6.0 + +## TL;DR + +DotKernel 1.6.0 no longer ships with a working built-in mobile detection method, because mobile detection now relies on the new Wurfl Cloud integration and must be configured via a Wurfl Cloud account and API key. +The old Dot_UserAgent_Wurfl class was removed and replaced by Dot_UserAgent_WurflCloud, which uses the Wurfl Cloud API adapter. +The article walks through the application.ini settings and shows sample code for reading device info and redirecting mobile visitors. + +The new DotKernel version 1.6.0 is coming with some changes to how we detect mobile devices; these changes are because of the new Wurfl Cloud integration. +This version of DotKernel no longer comes with a working built-in method for mobile detection, so first we have to configure it. + +- Go to the scientiamobile website and register for a Wurfl Cloud account. +- Choose device_os and mobile_browser for your account and save. +- Go to API Keys and copy the right key into application.ini. + +We chose device_os and mobile_browser capabilities because with these two capabilities we can get some extra capabilities (isMobile, isSmartPhone, isIphone, isAndroid, isBlackberry, isSymbian, and isWindowsMobile) using our built-in methods. +Choosing other capabilities from scientiamobile will result in wrong detection of these extra capabilities, but you can get only those capabilities using another method from the Dot_UserAgent_WurflCloud class. + +Wurfl Cloud setting in application.ini: + +```ini +resources.useragent.wurflcloud.active = TRUE +resources.useragent.wurflcloud.redirect = TRUE +resources.useragent.wurflcloud.cache = TRUE +resources.useragent.wurflcloud.cache_lifetime = 3600 +resources.useragent.wurflcloud.cache_namespace = WURFLCLOUD +resources.useragent.wurflcloud.api_key = 000000:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +resources.useragent.wurflcloud.lib_dir = APPLICATION_PATH "/library/WurflCloud/" +``` + +- active - used to turn on (TRUE) or off (FALSE) the Wurfl Cloud detection (default: TRUE). +- redirect - if TRUE, visitors from the frontend will be redirected to the mobile module (default: TRUE). +- cache - caches every distinct result to optimize the number of requests to scientiamobile (default: TRUE). +- cache_lifetime - time in seconds to keep the results in cache (default: 3600). +- cache_namespace - the prefix used for cache keys (default: WURFLCLOUD). +- api_key - the API key from your WURFL Cloud account (change this to your own key). +- lib_dir - the Wurfl Cloud library location in DotKernel (don't change this, unless you want to move the library). + +Because of these changes, we removed the old Dot_UserAgent_Wurfl class and added the new Dot_UserAgent_WurflCloud class, which uses the Wurfl Cloud API adapter. + +## Example of Dot_UserAgent Usage in DotKernel + +Get Wurfl configuration: + +```php +$wurflConf = $registry->configuration->resources->useragent->wurflcloud; +``` + +Note: you can have more Wurfl configurations if you have more libraries, like the Wurfl Package (GPL). + +If Wurfl is active, then get device info: + +```php +if($wurflConf->active) +{ + $deviceInfo = Dot_UserAgent :: getDeviceInfo($_SERVER); + ... +} +``` + +If the detected device is a mobile device, we save the device info in the database and redirect it to the mobile controller: + +```php +if( (0 < count((array)$deviceInfo)) && $deviceInfo->isMobile) +{ + + if(!$registry->session->visitId) + { + $registry->session->visitId = Dot_Statistic::registerVisit(); + } + + // if the Statistic module is integrate, record the deviceInfo too, and record TRUE + //in $session->mobile + if(!$registry->session->mobile) + { + $registry->session->mobile = + Dot_Statistic::registerMobileDetails($registry->session->visitId, $deviceInfo); + + //redirect to mobile controller , only if the session is not set. + //Otherwise will trap the user in mobile controller + if($wurflConf->redirect) + { + header('location: '. + $registry->configuration->website->params->url.'/mobile'); + exit; + } + } +} +``` + +## FAQ + +**Q: Why did mobile detection change in DotKernel 1.6.0?** +A: Because of the new Wurfl Cloud integration. This version no longer ships with a working built-in method for mobile detection, so it must be configured first. + +**Q: What are the steps to configure Wurfl Cloud detection?** +A: Go to the scientiamobile website and register for a Wurfl Cloud account, choose the device_os and mobile_browser capabilities for the account and save, then go to API Keys and copy the key into application.ini. + +**Q: Why choose the device_os and mobile_browser capabilities specifically?** +A: With these two capabilities, DotKernel's built-in methods can also derive extra capabilities such as isMobile, isSmartPhone, isIphone, isAndroid, isBlackberry, isSymbian, and isWindowsMobile. Choosing other capabilities from scientiamobile results in wrong detection of these extra capabilities. + +**Q: What happened to the old Dot_UserAgent_Wurfl class?** +A: It was removed and replaced with the new Dot_UserAgent_WurflCloud class, which uses the Wurfl Cloud API adapter. + +**Q: What does the redirect setting in application.ini control?** +A: When resources.useragent.wurflcloud.redirect is TRUE (the default), visitors from the frontend are redirected to the mobile module the first time a mobile device is detected. diff --git a/public/llms-content/dotkernel/disable-wurfl-redirect-for-mobile-browsers.md b/public/llms-content/dotkernel/disable-wurfl-redirect-for-mobile-browsers.md new file mode 100644 index 00000000..83292ee5 --- /dev/null +++ b/public/llms-content/dotkernel/disable-wurfl-redirect-for-mobile-browsers.md @@ -0,0 +1,42 @@ +--- +title: "Disable Wurfl redirect for mobile browsers" +description: "Shows the application.ini setting introduced in revision 408 to control DotKernel's automatic Wurfl-based redirect of mobile visitors to the mobile site, and the code that checks it." +author: "Adrian" +date_published: "2011-01-31" +canonical_url: "https://www.dotkernel.com/dotkernel/disable-wurfl-redirect-for-mobile-browsers/" +category: "Dotkernel" +language: "en" +--- + +# Disable Wurfl redirect for mobile browsers + +## TL;DR + +DotKernel's example mobile site normally relies on Wurfl to detect mobile browsers and automatically redirect visitors there on their first homepage view, which isn't always desired. +As of revision 408, this behavior is controlled by a single `resources.useragent.wurflapi.redirect` setting in application.ini. +The article shows that setting along with the matching condition in `IndexController.php` that checks it before registering and redirecting a visit. + +DotKernel has an example mobile site at [http://v1.dotkernel.net/mobile](http://v1.dotkernel.net/mobile) that uses [jQuery Mobile](http://jquerymobile.com/). +Wurfl is also used to detect mobile browsers (as discussed in a [previous blog post](http://www.dotkernel.com/dotkernel/wurfl-zend-framework-integration-into-dotkernel/)) and automatically redirect them to the mobile site the first time they view the homepage. +Sometimes this behavior isn't desired (for example when you don't have a mobile site, or you don't plan on using Wurfl at all). + +Starting with revision 408, there's an option in application.ini to disable the automatic redirect (by default the redirect is disabled): + +```ini +resources.useragent.wurflapi.redirect = false +``` + +The following condition is also added to Controllers/frontend/IndexController.php (at line 19) to check the configuration: + +```php +//if automatic redirect is enabled in application.ini and the browser is mobile and session->mobileHit is not set, register it and redirect +if($config->resources->useragent->wurflapi->redirect && 'mobile' == Dot_Kernel::getDevice()->getType() && !isset($session->mobileHit)) +``` + +## FAQ + +**Q: How do you disable the automatic mobile redirect in DotKernel?** +A: Starting with revision 408, set resources.useragent.wurflapi.redirect = false in application.ini. Per the article, this is also the default state of the redirect option. + +**Q: Where in the code is this configuration option checked?** +A: In Controllers/frontend/IndexController.php (around line 19), a condition checks whether the redirect is enabled in application.ini, whether the visiting browser is mobile, and whether session->mobileHit isn't already set, before registering and redirecting the visit. diff --git a/public/llms-content/dotkernel/disambiguation-dotkernel-1-and-dotkernel-3.md b/public/llms-content/dotkernel/disambiguation-dotkernel-1-and-dotkernel-3.md new file mode 100644 index 00000000..b34f7e71 --- /dev/null +++ b/public/llms-content/dotkernel/disambiguation-dotkernel-1-and-dotkernel-3.md @@ -0,0 +1,72 @@ +--- +title: "Disambiguation: DotKernel 1 and DotKernel 3" +description: "Clarifies what DotKernel 1 and DotKernel 3 are, how they differ architecturally, and which version is meant when someone simply says 'DotKernel'." +author: "Gabi DJ" +date_published: "2017-04-24" +canonical_url: "https://www.dotkernel.com/dotkernel/disambiguation-dotkernel-1-and-dotkernel-3/" +category: "Dotkernel" +language: "en" +--- + +# Disambiguation: DotKernel 1 and DotKernel 3 + +## TL;DR + +DotKernel 1 is the original PHP Application Framework built on Zend Framework 1 with an MVC architecture, released in 2010 and now in bugfix-only mode at version 1.8 LTS. +DotKernel 3 is a newer collection of PSR-7 middleware applications built on the Zend Expressive microframework and Zend Framework 3 components, implementing PSR-1, PSR-2, PSR-4, PSR-7, and PSR-11. +Since DotKernel 3's release, the unqualified name "DotKernel" refers to DotKernel 3, while DotKernel 1 is always referenced explicitly. + +## What Is DotKernel? + +The name DotKernel symbiotically combines the string Dot, as a representation of the Internet, and Kernel, the quintessence of any IT application. +In other words, DotKernel wishes to be, with modesty, the central part of Internet development, ensuring increased development productivity and run-time performance. + +## What Is DotKernel 1? + +DotKernel 1 is a PHP Application Framework, built on top of Zend Framework 1 (ZF1). +It had its first public release in July 2010. +It is tightly coupled with Zend Framework 1, and adds a set of custom or external features (such as Router, Template Engine, etc.). +It is composed of Zend Framework 1 and a set of custom or external features (such as Router, Template Engine, etc.). +DotKernel 1's architecture is based on MVC. + +The latest version is 1.8 Long Term Support. +No new version will be released anymore, only bugfixes. + +## What Is DotKernel 3? + +A collection of PSR-7 Middleware applications built on top of the [Zend Expressive](https://docs.zendframework.com/zend-expressive/) microframework. +It is composed of a set of custom and extended [Zend Framework 3](https://framework.zend.com/) components. +DotKernel 3's architecture is based on Middleware. +DotKernel implements the following PSRs: PSR-1, PSR-2, PSR-4, PSR-7, PSR-11. + +Currently there are 2 applications: Frontend and Admin, and a 3rd one is under development: API. + +## DotKernel = DotKernel 1 or DotKernel 3? + +In posts older than 2017, DotKernel 1 was referred to as DotKernel, because it was the only DotKernel version. +Since the release of DotKernel 3, it is referred to as DotKernel 3 or DotKernel. +All future references to DotKernel 1 will be explicitly made. + +### As of DotKernel 3 Release: + +DotKernel 1 = DotKernel 1 +DotKernel 3 = DotKernel 3 + +#### DotKernel = DotKernel 3 + +## FAQ + +**Q: What does the name "DotKernel" mean?** +A: It combines "Dot", as a representation of the Internet, with "Kernel", the quintessence of any IT application, reflecting the aim of being a central part of Internet development. + +**Q: What is DotKernel 1?** +A: A PHP Application Framework built on top of Zend Framework 1, first publicly released in July 2010, with an architecture based on MVC. Its latest version is 1.8 Long Term Support, which per the article will not be followed by a new version, only bugfixes. + +**Q: What is DotKernel 3?** +A: A collection of PSR-7 Middleware applications built on top of the Zend Expressive microframework, composed of a set of custom and extended Zend Framework 3 components, with an architecture based on Middleware. It implements PSR-1, PSR-2, PSR-4, PSR-7, and PSR-11. + +**Q: How many applications make up DotKernel 3?** +A: At the time of the article, there were two available applications, Frontend and Admin, with a third one, API, under development. + +**Q: When someone writes just "DotKernel", which version is meant?** +A: In posts older than 2017, "DotKernel" referred to DotKernel 1, since it was the only version. Since the release of DotKernel 3, "DotKernel" refers to DotKernel 3, and all future references to DotKernel 1 are made explicitly. diff --git a/public/llms-content/dotkernel/doctrine-cache-using-symfony-cache.md b/public/llms-content/dotkernel/doctrine-cache-using-symfony-cache.md new file mode 100644 index 00000000..48b7b394 --- /dev/null +++ b/public/llms-content/dotkernel/doctrine-cache-using-symfony-cache.md @@ -0,0 +1,144 @@ +--- +title: "Doctrine cache using symfony/cache" +description: "How to enable and configure the dotkernel/dot-cache component, a wrapper around symfony/cache, to cache Doctrine's result, metadata, query, and hydration data in DotKernel Admin." +author: "MarioRadu" +date_published: "2024-02-27" +canonical_url: "https://www.dotkernel.com/dotkernel/doctrine-cache-using-symfony-cache/" +category: "Dotkernel" +language: "en" +--- + +# Doctrine cache using symfony/cache + +## TL;DR + +Caching stores data the first time it's requested so that later requests can be served from the cache instead of the original, slower source, which improves response times. +This article, a follow-up to an earlier caching article, shows how to enable the dot-cache component, a wrapper around symfony/cache, in DotKernel Admin. +It covers the array and filesystem storage adapters, configuring Doctrine's four cache types (result, metadata, query, hydration), and marking entities and queries as cacheable. + +## Installation + +Run the following command in your project directory: + +```bash +composer require dotkernel/dot-cache +``` + +After installing, add the `DotCacheConfigProvider::class` class to your configuration aggregate (config/config.php). +Before continuing with the configuration process, it helps to know a few things about how and where the data is stored. +The [dotkernel/dot-cache](https://packagist.org/packages/dotkernel/dot-cache) component is a wrapper that sits on top of [symfony/cache](https://packagist.org/packages/symfony/cache). +It currently supports two adapters and can store data in two distinct locations: + +- array - stores data in-memory +- filesystem - stores data on local disk files + +1. Storing data in-memory is the fastest and sometimes the cheapest caching mechanism, but it also comes with down-sides. +Storing everything in RAM memory is not the best idea when your application is running on a low memory system. +In this case you should consider using the filesystem mechanism. + +2. The second caching mechanism involves storing data into files on the local disk, known as the filesystem option. +While this option may be slightly slower than the first one, it provides a more persistent storage solution. + +Feel free to explore and use other adapters from [symfony/cache](https://packagist.org/packages/symfony/cache) by checking the [official documentation](https://symfony.com/doc/current/components/cache.html#advanced-usage). + +## Configuration + +In `config/autoload/doctrine.global.php`, in the `doctrine.configuration.orm_default` key add the following entry: + +```php +'result_cache' => 'filesystem', +'metadata_cache' => 'filesystem', +'query_cache' => 'filesystem', +'hydration_cache' => 'array', +'second_level_cache' => , +], +``` + +Next, under the `doctrine` key add the following items: + +```php +'cache' => , + 'filesystem' => , +], +``` + +The result is that the metadata and query cache will be stored in the `data/cache/doctrine` folder and the hydration cache will be stored in-memory. +Each system is unique, requiring customized configurations. +Make sure to identify the specific configuration requirements for your application. +Doctrine cache is divided into 4 different types: + +- `result_cache` +- `metadata_cache` +- `query_cache` +- `hydration_cache` + +### Result Cache + +The result cache can be used to store the results of your queries, enabling Doctrine to avoid querying the database or hydrating the data again after the initial retrieval. + +### Metadata Cache + +Parsing your class metadata on every request is inefficient. +Instead, it's advisable to cache this information using one of the available cache adapters. + +### Query Cache + +In a production environment, it's strongly recommended to cache the resulting DQL query into its SQL equivalent. +Since the query doesn't change unless the DQL query itself changes, it's unnecessary to parse it multiple times. + +### Hydration Cache + +Doctrine hydration cache is a feature that stores the results of data hydration, which is the process of converting raw database data into usable objects or arrays. +By caching these results, it avoids repeating the hydration process for repeated queries, improving performance. + +## How to Use + +To enable caching for entities, you need to add the `#` attribute like in the following example: + +```php +# +# +# +class Admin extends AbstractEntity implements AdminInterface +{ +} +``` + +For further details about the cache mode please refer to the [official documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/3.0/reference/second-level-cache.html). +When querying data, you can have Doctrine cache your results. +You do this by calling the `setCacheable` method on the query builder. + +```php +$this->getQueryBuilder() + ->select('admin') + ->from(Admin::class, 'admin') + ->setCacheable(true) + ->getQuery() + ->getResult(); +``` + +Caching is not limited to entities alone. +Objects can be cached too. +Check the [basic cache usage](https://symfony.com/doc/current/components/cache.html#basic-usage-psr-6) for this purpose. +In conclusion, cache plays a vital role in optimizing system performance and improving user experience by storing frequently accessed data. +As technology continues to evolve, caching mechanisms will remain an integral part of modern computing architectures, driving faster access to data and smoother user interactions across various digital platforms. + +## FAQ + +**Q: What component does this article use for caching Doctrine data?** +A: The dotkernel/dot-cache component, a wrapper that sits on top of symfony/cache. It's installed with composer require dotkernel/dot-cache and registered by adding DotCacheConfigProvider::class to the configuration aggregate. + +**Q: What storage adapters does dot-cache currently support?** +A: Two: array, which stores data in-memory and is the fastest option but uses more RAM, and filesystem, which stores data in local disk files and is slightly slower but more persistent. + +**Q: What are the four types of Doctrine cache covered?** +A: result_cache, metadata_cache, query_cache, and hydration_cache, each configured under the doctrine.configuration.orm_default key. + +**Q: What does the result cache do?** +A: It stores the results of queries, letting Doctrine avoid querying the database or hydrating the data again after the initial retrieval. + +**Q: Why cache class metadata?** +A: Because parsing class metadata on every request is inefficient, so it's advisable to cache it using one of the available cache adapters. + +**Q: How do you mark an entity or a query as cacheable?** +A: To enable caching for entities you add a caching attribute to the entity class; to cache an individual query, call setCacheable(true) on the query builder before getResult(). diff --git a/public/llms-content/dotkernel/doctrine-enum-implementation-in-dotkernel.md b/public/llms-content/dotkernel/doctrine-enum-implementation-in-dotkernel.md new file mode 100644 index 00000000..e8720a93 --- /dev/null +++ b/public/llms-content/dotkernel/doctrine-enum-implementation-in-dotkernel.md @@ -0,0 +1,276 @@ +--- +title: "Doctrine enum implementation in Dotkernel" +description: "How Dotkernel adopted Doctrine ORM 3.2's EnumType support to replace loosely-enforced string-based status columns with PHP enums backed by a custom DBAL type." +author: "Florin Bidirean" +date_published: "2024-11-05" +canonical_url: "https://www.dotkernel.com/dotkernel/doctrine-enum-implementation-in-dotkernel/" +category: "Dotkernel" +language: "en" +--- + +# Doctrine enum implementation in Dotkernel + +## TL;DR + +Doctrine ORM 3.2.0 added EnumType columns, building on the enum type introduced in PHP 8.1, and Dotkernel now implements this on both the PHP and database sides. +The article contrasts Dotkernel's old string-based flag columns (like `User->Status`) with a new setup that uses custom PHP enums paired with a DBAL type extending `AbstractEnumType`. +The new approach creates an explicit, enforced link between the PHP code and the database column values, at the cost of needing to update both sides whenever the value set changes. + +## Doctrine's Approach + +The update introduces the detection of `enumType` and `options.values` from a property with `type: Types::ENUM`. +[This PR](https://github.com/doctrine/orm/pull/11666) discusses the update and links to several older relevant issues. + +### Old Setup + +```php +# +class Card +{ + # + # + # + public int $id; + + #], + )] + public Suit $suit; +} +``` + +### New Setup + +```php +# +class Card +{ + # + # + # + public int $id; + + # + public Suit $suit; +} +``` + +Note that the type `Types::ENUM` part is still required if we want to have an actual `enum` column in MySQL/MariaDB. +We still default to `Types::STRING` or `Types::INTEGER` for column types with a PHP enum, as this is the more portable solution and the safer default. + +## Dotkernel's Approach + +### Old Setup + +Dotkernel uses flags for columns like `User->Status`, but we resorted to the simpler `string` type. +The obvious disadvantage is that you can't definitively enforce a set of values for a given column. +Sure, the PHP can be set up to only use the agreed-upon set of values, but the database is independent from it. +If you edit a value manually in the database, any string is accepted. + +The issue is the same on the side of the PHP code. +If the developer adds a value with a typo, it's supported, but will not work as intended. + +The only advantage this setup has is the ability to easily add more values in the value set. +This may be seen as a feature, but it invites bugs in the execution. + +Our old implementation defined the values like below, for the `User` entity. + +```php +public const STATUS_PENDING = 'pending'; +public const STATUS_ACTIVE = 'active'; +public const STATUSES = ; +``` + +The column for the ORM was defined like this, as a simple string, with `pending` as its default value: + +```php +# +protected string $status = self::STATUS_PENDING; +``` + +Obviously, the `getStatus` and `setStatus` also work with strings: + +```php +public function getStatus(): string +{ + return $this->status; +} + +public function setStatus(string $status): self +{ + $this->status = $status; +} +``` + +### New Setup + +Thanks to the update of `doctrine/orm` to version 3.2.0, Dotkernel can now have a proper link between the PHP code and database values. +Now the link between the PHP code and the database is explicit and enforced. + +Any update to the value set must be on both the PHP code and the database. + +Let's review how the update affects the `User` entity. + +In the next example, we show how to implement a value set using a custom enum. + +First, we define our custom value set in `src/User/src/Enum/UserStatusEnum.php`. + +```php +namespace Api\User\Enum; + +enum UserStatusEnum: string +{ + case Active = 'active'; + case Pending = 'pending'; +} +``` + +We need to create `src/User/src/DBAL/Types/UserStatusEnumType.php` to process the new values for the `status` column. + +`AbstractEnumType` must be extended by any future custom enum type. + +```php +namespace Api\User\DBAL\Types; + +use Api\App\DBAL\Types\AbstractEnumType; +use Api\User\Enum\UserStatusEnum; + +class UserStatusEnumType extends AbstractEnumType +{ + public const NAME = 'user_status_enum'; + + protected function getEnumClass(): string + { + return UserStatusEnum::class; + } + + public function getName(): string + { + return self::NAME; + } +} +``` + +If you create your own enum types, make sure to update the `NAME` constant and the value returned by `getEnumClass`. + +Let's register the custom type in `config/autoload/doctrine.global.php` under the `types` key: + +```php +'types' => + UserStatusEnumType::NAME => UserStatusEnumType::class, + +], +``` + +The filtering is updated in `src/User/src/InputFilter/Input/StatusInput.php`: + +```php +$this->getFilterChain() + ->attachByName(StringTrim::class) + ->attachByName(StripTags::class) + ->attach(fn($value) => $value === null ? UserStatusEnum::Active : UserStatusEnum::from($value)); + +$this->getValidatorChain() + ->attachByName(InArray::class, , true); +``` + +The above ensures that the new `UserStatusEnum` class is used for the `status` column updates. + +The `User` entity uses the new `UserStatusEnum` class. + +```php +#)] +protected UserStatusEnum $status = UserStatusEnum::Pending; +``` + +The `status` getter and setter are also updated: + +```php +public function getStatus(): UserStatusEnum +{ + return $this->status; +} + +public function setStatus(UserStatusEnum $status): self +{ + $this->status = $status; +} +``` + +Dotkernel checks the user status during login in `src/User/src/Repository/UserRepository.php`. +If the user is not activated, the login is rejected. + +```php +if ($clientEntity->getName() === 'frontend' && $result !== UserStatusEnum::Active) { + throw new OAuthServerException(Message::USER_NOT_ACTIVATED, 6, 'inactive_user', 401); +} +``` + +A new user is created using the `enum` type and `pending` as the default. + +```php +$user = (new User()) + ->setDetail($detail) + ->setIdentity($data) + ->usePassword($data) + ->setStatus($data ?? UserStatusEnum::Pending); +``` + +Note the `status` column in the migration query which now looks like this: + +```php +$this->addSql(' +CREATE TABLE user ( + uuid BINARY(16) NOT NULL, + identity VARCHAR(191) NOT NULL, + password VARCHAR(191) NOT NULL, + status ENUM(\'active\', \'pending\') DEFAULT \'pending\' NOT NULL, + isDeleted TINYINT(1) NOT NULL, + hash VARCHAR(64) NOT NULL, + created DATETIME NOT NULL, + updated DATETIME DEFAULT NULL, + UNIQUE INDEX UNIQ_8D93D6496A95E9C4 (identity), UNIQUE INDEX UNIQ_8D93D649D1B862B8 (hash), + PRIMARY KEY(uuid)) DEFAULT CHARACTER SET utf8mb4'); +``` + +The difference for the migration query is for the `status` column, highlighted below: + +``` +old setup: status VARCHAR(20) NOT NULL +new setup: status ENUM(\'active\', \'pending\') DEFAULT \'pending\' NOT NULL +``` + +## Conclusions + +The old setup used in the Dotkernel applications worked fine, but the limitations were clear as day. +There was: + +- No enforcement of the value set. +- No link between the PHP code and the database. + +The new setup solves both issues, ensuring more consistent flag management for your classes. + +## FAQ + +**Q: What update triggered this change to Dotkernel's enum handling?** +A: The update of doctrine/orm to version 3.2.0 introduced EnumType columns, building on the enum type introduced in PHP 8.1. Dotkernel implemented this new data type on both the PHP side and the database side. + +**Q: What was the limitation of Dotkernel's old approach to columns like User->Status?** +A: The old setup used a simple string type, so the value set couldn't be definitively enforced. A typo in a PHP value would still be accepted, and the database was independent of any values the PHP code allowed, so manually editing a value in the database would accept any string. + +**Q: What was the one advantage of the old string-based setup?** +A: It made it easy to add more values to the value set, though the article notes this ease also invites bugs in the execution. + +**Q: What do you need to create to add a new custom enum type?** +A: A PHP enum class (like UserStatusEnum) plus a DBAL type class extending AbstractEnumType, which must define a NAME constant and a getEnumClass() method; the new type is then registered under the types key in config/autoload/doctrine.global.php. + +**Q: Does Types::ENUM still fall back to a string or integer database column?** +A: The article notes that Doctrine still defaults to Types::STRING or Types::INTEGER for columns backed by a PHP enum, as this is considered the more portable and safer default; Types::ENUM is required if you want an actual enum column in MySQL/MariaDB. + +**Q: What must happen when the value set of an enum changes under the new setup?** +A: Any update to the value set must be made on both the PHP code and the database, since the new setup creates an explicit, enforced link between them. + +## Resources + +- [Dotkernel API Pull Request](https://github.com/dotkernel/api/pull/339/files) +- [Doctrine Pull Request](https://github.com/doctrine/orm/pull/11666) +- [PHP Enumerations](https://www.php.net/manual/en/language.enumerations.overview.php) diff --git a/public/llms-content/dotkernel/dotboost-technologies-products-and-services-north-american-relaunch.md b/public/llms-content/dotkernel/dotboost-technologies-products-and-services-north-american-relaunch.md new file mode 100644 index 00000000..61aa68fb --- /dev/null +++ b/public/llms-content/dotkernel/dotboost-technologies-products-and-services-north-american-relaunch.md @@ -0,0 +1,43 @@ +--- +title: "DotBoost Technologies : Products and Services North American Relaunch" +description: "Dotboost Technologies announces its North American relaunch, centered on the source release of its in-house DotKernel framework alongside expanded IT integration and consulting services." +author: "admin" +date_published: "2010-01-28" +canonical_url: "https://www.dotkernel.com/dotkernel/dotboost-technologies-products-and-services-north-american-relaunch/" +category: "Dotkernel" +language: "en" +--- + +# DotBoost Technologies : Products and Services North American Relaunch + +## TL;DR + +Dotboost announces its North American relaunch, aimed at better serving clients in Canada and the US. +The relaunch centers on the source release of its in-house DotKernel framework, along with expanded business IT integration and clearer consulting services. +Founded in 2005, Dotboost describes itself as treating clients as strategic partners rather than as a typical IT vendor. + +## The North American Relaunch + +A new style and advanced approach to accompany the Dotkernel source release. + +Dotboost is pleased to announce our North American Relaunch. +This new phase comes as a result of dedicated research and analysis on how to best serve clients in Canada and the US. + +At the heart of our relaunch is the source release for our exclusive inhouse developed DotKernel framework. +We have also added business IT integration and increased the clarity to our existing consulting services. + +## The Dotboost Approach + +We're not your average IT organization; we view our customers as strategic partners. +This paradigm allows us to take a comprehensive approach towards creating solutions and gain the competitive advantage. + +Founded in 2005, the Dotboost process can incorporate anywhere into your project's life-cycle including concept development, architecture and design, development and integration, and implementation and support. +We use time and distance to our advantage, pushing competitive boundaries and staking our place as a globally efficient organization. + +## FAQ + +**Q: What is at the heart of Dotboost's North American relaunch?** +A: The source release of Dotboost's exclusive, in-house developed DotKernel framework, along with added business IT integration and increased clarity around existing consulting services. + +**Q: When was Dotboost founded, and at what stages can it join a project?** +A: Dotboost was founded in 2005. Per the article, its process can incorporate anywhere into a project's life-cycle, including concept development, architecture and design, development and integration, and implementation and support. diff --git a/public/llms-content/dotkernel/dotkernel-1-2-0-release.md b/public/llms-content/dotkernel/dotkernel-1-2-0-release.md new file mode 100644 index 00000000..dda834f2 --- /dev/null +++ b/public/llms-content/dotkernel/dotkernel-1-2-0-release.md @@ -0,0 +1,63 @@ +--- +title: "DotKernel 1.2.0 release" +description: "Release notes for DotKernel 1.2.0, covering database naming convention changes, the new 'dots' submodule concept, new and updated library classes, and the use of prepared statements for all SQL queries." +author: "Teo" +date_published: "2010-07-05" +canonical_url: "https://www.dotkernel.com/dotkernel/dotkernel-1-2-0-release/" +category: "Dotkernel" +language: "en" +--- + +# DotKernel 1.2.0 release + +## TL;DR + +DotKernel 1.2.0 has been released, bringing changes since the previous 1.1.2 release. +The database tables were renamed and restructured to follow database naming conventions, and configuration for each "dots" (submodule) now lives in XML files instead of being hard-coded in PHP. +The release also adds new library classes (Dot_Geoip, Dot_Seo), updates existing ones (Dot_Curl, Dot_Session), and confirms that all SQL queries are written as prepared statements. + +## Database Naming Conventions + +On database, we changed the names and structure of tables to respect database naming convention. +See [http://www.dotkernel.com/dotkernel/dotkernel-database-naming-conventions-for-mysql/](http://www.dotkernel.com/dotkernel/dotkernel-database-naming-conventions-for-mysql/) for details. + +## The "Dots" Concept + +A new word came into our DotKernel discussions: dots. +We use this term when talking about a submodule and all its component files. +For example, "user" is a submodule of the frontend module. +Note that one dots can be part of multiple modules (for example, "user" dots belong to both the frontend and admin module). +For each dots, the configuration values have been added to XML files which are stored in the configs/dots folder. +In the previous versions, these values were hard-coded in the PHP files. + +Another change made in the configs folder is resource.xml, which contains the configuration values for the controllers of each module. + +To be easier to start an application from DotKernel, in the admin module, there are now the following dots: admin, user and system. + +## Library Class Updates + +New library classes have been implemented: Dot_Geoip and Dot_Seo, and some of the existing ones have been updated: Dot_Curl and Dot_Session (each module has its own session). + +## SQL Prepared Statements + +In DotKernel, all SQL queries are written as prepared statements. +We strongly encourage this practice: [http://www.dotkernel.com/php-development/protection-against-sql-injection-using-pdo-and-zend-framework/](http://www.dotkernel.com/php-development/protection-against-sql-injection-using-pdo-and-zend-framework/) + +For more details, see [ChangeLog 1.2.0](http://www.dotkernel.com/changelog/1-2-0/). + +## FAQ + +**Q: What is a "dots" in DotKernel, a term introduced in this release?** +A: A term for a submodule and all its component files. For example, "user" is a dots of the frontend module, and one dots can belong to multiple modules, such as "user" belonging to both frontend and admin. + +**Q: Where are dots configuration values stored, compared to earlier versions?** +A: They're stored in XML files inside the configs/dots folder. In previous versions, these values were hard-coded in the PHP files. + +**Q: What dots does the admin module include by default?** +A: admin, user, and system, to make it easier to start an application from DotKernel. + +**Q: What library classes were added or updated in 1.2.0?** +A: Dot_Geoip and Dot_Seo were newly implemented, while Dot_Curl and Dot_Session were updated, with each module now having its own session. + +**Q: How are SQL queries written in DotKernel?** +A: All SQL queries are written as prepared statements, a practice the article strongly encourages. diff --git a/public/readme/dotkernel/dotkernel-1-2-2-release.md b/public/llms-content/dotkernel/dotkernel-1-2-2-release.md similarity index 88% rename from public/readme/dotkernel/dotkernel-1-2-2-release.md rename to public/llms-content/dotkernel/dotkernel-1-2-2-release.md index 3a9afd1e..0dcc1e93 100644 --- a/public/readme/dotkernel/dotkernel-1-2-2-release.md +++ b/public/llms-content/dotkernel/dotkernel-1-2-2-release.md @@ -12,7 +12,8 @@ language: "en" ## TL;DR -DotKernel 1.2.2 is a bug-fix release that closes five tracked issues. Because one of the fixes updated the copyright line, every PHP file in the codebase changed, so the full release or the incremental upgrade package is needed. +DotKernel 1.2.2 is a bug-fix release that closes five tracked issues. +Because one of the fixes updated the copyright line, every PHP file in the codebase changed, so the full release or the incremental upgrade package is needed. ## Bug fixes in 1.2.2 @@ -26,7 +27,8 @@ DotKernel 1.2.2 is a bug-fix release that closes five tracked issues. Because on ## Upgrading -To get only the changed files from 1.2.1 to 1.2.2, download the upgrade package (linked in the post) instead of the full distribution. Full details are available in the ChangeLog 1.2.2, and further changes can be tracked on the DotKernel Tracker or DotKernel WebSVN. +To get only the changed files from 1.2.1 to 1.2.2, download the upgrade package (linked in the post) instead of the full distribution. +Full details are available in the ChangeLog 1.2.2, and further changes can be tracked on the DotKernel Tracker or DotKernel WebSVN. Note also that DotKernel 1.2.1 had been released a few days earlier, on July 22, 2010, with its own ChangeLog and upgrade package. diff --git a/public/readme/dotkernel/dotkernel-1-3-0-release.md b/public/llms-content/dotkernel/dotkernel-1-3-0-release.md similarity index 87% rename from public/readme/dotkernel/dotkernel-1-3-0-release.md rename to public/llms-content/dotkernel/dotkernel-1-3-0-release.md index 2b90b12f..9142781c 100644 --- a/public/readme/dotkernel/dotkernel-1-3-0-release.md +++ b/public/llms-content/dotkernel/dotkernel-1-3-0-release.md @@ -12,13 +12,15 @@ language: "en" ## TL;DR -DotKernel 1.3.0 brings a switchable admin skin, a way to protect member-only pages, a rename of Dot_Sessions, and a reorganization of resource.xml into route.xml and dots.xml. Because of that XML reorganization, 1.3.0 is not backward compatible with earlier versions. +DotKernel 1.3.0 brings a switchable admin skin, a way to protect member-only pages, a rename of Dot_Sessions, and a reorganization of resource.xml into route.xml and dots.xml. +Because of that XML reorganization, 1.3.0 is not backward compatible with earlier versions. ## Highlights ### Admin skin switcher -The admin skin can now be customized. Several ready-made skins are available: blue, brown, gray, and green. Set the skin by changing the `settings.admin.skin` value (e.g. `settings.admin.skin = green`). +The admin skin can now be customized. Several ready-made skins are available: blue, brown, gray, and green. +Set the skin by changing the `settings.admin.skin` value (e.g. `settings.admin.skin = green`). ### Protecting member-only links @@ -38,7 +40,8 @@ The release also closed a number of other tracked issues, covering: the Dot_Sess ## Compatibility note -Because of the XML file reorganization, this release is **not compatible** with previous versions. Further details on what changed are available on the DotKernel Tracker or DotKernel WebSVN. +Because of the XML file reorganization, this release is **not compatible** with previous versions. +Further details on what changed are available on the DotKernel Tracker or DotKernel WebSVN. ## FAQ diff --git a/public/readme/dotkernel/dotkernel-1-3-2-release.md b/public/llms-content/dotkernel/dotkernel-1-3-2-release.md similarity index 92% rename from public/readme/dotkernel/dotkernel-1-3-2-release.md rename to public/llms-content/dotkernel/dotkernel-1-3-2-release.md index 15d79b98..fba22134 100644 --- a/public/readme/dotkernel/dotkernel-1-3-2-release.md +++ b/public/llms-content/dotkernel/dotkernel-1-3-2-release.md @@ -44,7 +44,8 @@ A: It's mainly a maintenance release, containing many bug fixes, some refactorin A: Fixes include a CSS issue on the admin phpinfo page, a warning in the admin dashboard, a WURFL cache issue and WURFL version issue in admin, a Dot_Paginator bug, a Zend Paginator double-query issue, and a database naming convention issue. **Q: What minor features and refactoring were included?** -A: Minor features include a refactor of validIP in Dot_Kernel and showing the WURFL date and API version in admin. Refactoring covered Zend_Paginator and added a dojo dijit theme to DotKernel. +A: Minor features include a refactor of validIP in Dot_Kernel and showing the WURFL date and API version in admin. +Refactoring covered Zend_Paginator and added a dojo dijit theme to DotKernel. ## Resources diff --git a/public/readme/dotkernel/dotkernel-1-5-0-released.md b/public/llms-content/dotkernel/dotkernel-1-5-0-released.md similarity index 71% rename from public/readme/dotkernel/dotkernel-1-5-0-released.md rename to public/llms-content/dotkernel/dotkernel-1-5-0-released.md index a6ae6ecf..8826b426 100644 --- a/public/readme/dotkernel/dotkernel-1-5-0-released.md +++ b/public/llms-content/dotkernel/dotkernel-1-5-0-released.md @@ -12,7 +12,8 @@ language: "en" ## TL;DR -After a longer wait than usual and around 250 commits, DotKernel 1.5.0 was released, skipping 1.4 entirely due to the scale of changes. Highlights include switching from Dojo to jQuery, a redesigned admin and frontend, model inheritance through a new Dot_Model class, support for dashed controller names, and a reorganized Zend Registry. +After a longer wait than usual and around 250 commits, DotKernel 1.5.0 was released, skipping 1.4 entirely due to the scale of changes. +Highlights include switching from Dojo to jQuery, a redesigned admin and frontend, model inheritance through a new Dot_Model class, support for dashed controller names, and a reorganized Zend Registry. ## Why skip straight to 1.5.0? @@ -22,7 +23,8 @@ Due to the large amount of changes and the long time spent in development, the t ### Switched from Dojo to jQuery -Starting with 1.5.0, DotKernel switched from using Dojo to jQuery. Dojo can still be used in your own projects, but only jQuery is used and maintained in the DotKernel distribution itself. +Starting with 1.5.0, DotKernel switched from using Dojo to jQuery. +Dojo can still be used in your own projects, but only jQuery is used and maintained in the DotKernel distribution itself. ### New designs @@ -30,11 +32,14 @@ The admin site was redesigned, with new themes and a dropdown menu, along with a ### Model inheritance -Previously there was a lot of code duplication in models — for example, a `getUserById` function might exist separately in both the admin and frontend User models. To solve this, a `Dot_Model` class was introduced along with a way to define global models inherited by both admin and frontend. A `User` class in the admin only holds admin-specific methods, a `User` class in the frontend only holds frontend-specific methods, and both inherit a shared `Dot_Model_User` class containing the common code. +Previously there was a lot of code duplication in models — for example, a `getUserById` function might exist separately in both the admin and frontend User models. +To solve this, a `Dot_Model` class was introduced along with a way to define global models inherited by both admin and frontend. +A `User` class in the admin only holds admin-specific methods, a `User` class in the frontend only holds frontend-specific methods, and both inherit a shared `Dot_Model_User` class containing the common code. ### Dashed controllers -The way controller names are parsed was changed so that controllers with multiple words, split with dashes, work without breaking the coding standard. For example, `www.example.com/search-article` calls `SearchArticleController.php`. +The way controller names are parsed was changed so that controllers with multiple words, split with dashes, work without breaking the coding standard. +For example, `www.example.com/search-article` calls `SearchArticleController.php`. ### Zend Registry reorganization @@ -50,10 +55,12 @@ There were about 250 commits in the SVN repository since the previous release, s A: Because of the large amount of changes and the long time spent in development, the team chose to skip version 1.4 and go straight to 1.5.0. **Q: Did DotKernel switch from Dojo to jQuery in 1.5.0?** -A: Yes. Starting with 1.5.0, DotKernel switched from Dojo to jQuery for its own distribution, though Dojo can still be used in your own projects. +A: Yes. +Starting with 1.5.0, DotKernel switched from Dojo to jQuery for its own distribution, though Dojo can still be used in your own projects. **Q: What is Dot_Model and why was it introduced?** -A: Dot_Model is a base class introduced to reduce code duplication between admin and frontend models. Both admin- and frontend-specific model classes (such as User) inherit from a shared Dot_Model_User class that holds the common code. +A: Dot_Model is a base class introduced to reduce code duplication between admin and frontend models. +Both admin- and frontend-specific model classes (such as User) inherit from a shared Dot_Model_User class that holds the common code. **Q: How does the "dashed controllers" feature work?** A: The controller name parsing was changed so a URL like www.example.com/search-article correctly calls SearchArticleController.php, allowing multi-word controller names split with dashes without breaking the coding standard. diff --git a/public/readme/dotkernel/dotkernel-1-8-0-lts-released.md b/public/llms-content/dotkernel/dotkernel-1-8-0-lts-released.md similarity index 93% rename from public/readme/dotkernel/dotkernel-1-8-0-lts-released.md rename to public/llms-content/dotkernel/dotkernel-1-8-0-lts-released.md index a43cf51e..78996854 100644 --- a/public/readme/dotkernel/dotkernel-1-8-0-lts-released.md +++ b/public/llms-content/dotkernel/dotkernel-1-8-0-lts-released.md @@ -12,11 +12,14 @@ language: "en" ## TL;DR -DotKernel 1.8.0 (LTS) was released with a new Plugin Architecture, a redesigned and mobile-friendly frontend, APC/File caching for faster response times, a new Dot_Request class, and multiple security and alerting improvements. Some features (WURFL integration, multiple SMTP transporters) were removed from core and made available as plugins instead. +DotKernel 1.8.0 (LTS) was released with a new Plugin Architecture, a redesigned and mobile-friendly frontend, APC/File caching for faster response times, a new Dot_Request class, and multiple security and alerting improvements. +Some features (WURFL integration, multiple SMTP transporters) were removed from core and made available as plugins instead. ## What is LTS? -Long-term support (LTS) is a type of special version or edition of software designed to be supported for a longer than normal period. It's particularly applicable to open-source software projects. The 1.8.0 LTS release itself contains many bug fixes, some refactoring, and a few minor features. +Long-term support (LTS) is a type of special version or edition of software designed to be supported for a longer than normal period. +It's particularly applicable to open-source software projects. +The 1.8.0 LTS release itself contains many bug fixes, some refactoring, and a few minor features. ## Highlights of 1.8.0 (LTS) diff --git a/public/readme/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md b/public/llms-content/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md similarity index 95% rename from public/readme/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md rename to public/llms-content/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md index 276cd1bd..c519c3bc 100644 --- a/public/readme/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md +++ b/public/llms-content/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md @@ -12,7 +12,8 @@ language: "en" ## TL;DR -DotKernel 1.8.1 was released with Enhanced Cache Support, allowing cache tags to be used if the hosting environment supports them. A dedicated upgrade package is available for users coming from 1.8.0. +DotKernel 1.8.1 was released with Enhanced Cache Support, allowing cache tags to be used if the hosting environment supports them. +A dedicated upgrade package is available for users coming from 1.8.0. ## What's new diff --git a/public/readme/dotkernel/dotkernel-coding-standard.md b/public/llms-content/dotkernel/dotkernel-coding-standard.md similarity index 100% rename from public/readme/dotkernel/dotkernel-coding-standard.md rename to public/llms-content/dotkernel/dotkernel-coding-standard.md diff --git a/public/readme/dotkernel/dotkernel-database-naming-conventions-for-mysql.md b/public/llms-content/dotkernel/dotkernel-database-naming-conventions-for-mysql.md similarity index 89% rename from public/readme/dotkernel/dotkernel-database-naming-conventions-for-mysql.md rename to public/llms-content/dotkernel/dotkernel-database-naming-conventions-for-mysql.md index d23201a1..a1958877 100644 --- a/public/readme/dotkernel/dotkernel-database-naming-conventions-for-mysql.md +++ b/public/llms-content/dotkernel/dotkernel-database-naming-conventions-for-mysql.md @@ -12,7 +12,8 @@ language: "en" ## TL;DR -DotKernel's database naming conventions are borrowed from FaZend's "Rules of naming of database tables and columns." Tables use singular, camelLetter names, every table has an auto-increment id, foreign keys are named after the referenced table and column, and SQL keywords are capitalized. +DotKernel's database naming conventions are borrowed from FaZend's "Rules of naming of database tables and columns." +Tables use singular, camelLetter names, every table has an auto-increment id, foreign keys are named after the referenced table and column, and SQL keywords are capitalized. ## Database naming conventions for tables and columns @@ -64,7 +65,8 @@ A: They are borrowed from FaZend's "Rules of naming of database tables and colum A: Singular table names only, for example user, category, product, order, orderProduct. **Q: How should foreign key columns be named?** -A: A foreign key column takes the name of the referenced table plus the name of the referenced column. For example, referencing table admin's Id column produces a column named adminId. +A: A foreign key column takes the name of the referenced table plus the name of the referenced column. +For example, referencing table admin's Id column produces a column named adminId. **Q: What naming pattern is used for CONSTRAINT names?** A: The pattern is FK_referencedTableName_tableName, for example CONSTRAINT `FK_admin_adminLogin`. diff --git a/public/readme/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md b/public/llms-content/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md similarity index 75% rename from public/readme/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md rename to public/llms-content/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md index 69391177..10c743de 100644 --- a/public/readme/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md +++ b/public/llms-content/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md @@ -12,17 +12,21 @@ language: "en" ## TL;DR -Dotkernel Light is a version of Dotkernel Frontend that includes only the bare-bones essentials. It's built on the Mezzio microframework using Laminas components, and is designed as a presentation site, a fast-start introduction to Mezzio, or a clean starting point for a project where you want full control over functionality. +Dotkernel Light is a version of Dotkernel Frontend that includes only the bare-bones essentials. +It's built on the Mezzio microframework using Laminas components, and is designed as a presentation site, a fast-start introduction to Mezzio, or a clean starting point for a project where you want full control over functionality. ## Goal -Dotkernel Light is designed to be a fast-start example of using the Mezzio microframework, as well as an entry-level version of Dotkernel Frontend. Its purpose is to present the newbie developer with as few moving parts as possible, while also giving the more advanced developer a starting point with full control of the platform's functionality. +Dotkernel Light is designed to be a fast-start example of using the Mezzio microframework, as well as an entry-level version of Dotkernel Frontend. +Its purpose is to present the newbie developer with as few moving parts as possible, while also giving the more advanced developer a starting point with full control of the platform's functionality. -Light retains the modern architecture of Mezzio microframework and several Laminas components used in Dotkernel Frontend. The low number of out-of-the-box components encourages active exploration of the functionality required by your application — you add only the packages your application needs. +Light retains the modern architecture of Mezzio microframework and several Laminas components used in Dotkernel Frontend. +The low number of out-of-the-box components encourages active exploration of the functionality required by your application — you add only the packages your application needs. ## Components and functionality -Dotkernel Light is a stripped-down version of Dotkernel Frontend. Like Frontend, it is built on top of Mezzio microframework using Laminas components, but with limited features and a lower number of packages, which makes the learning curve of working with the repo considerably gentler. +Dotkernel Light is a stripped-down version of Dotkernel Frontend. +Like Frontend, it is built on top of Mezzio microframework using Laminas components, but with limited features and a lower number of packages, which makes the learning curve of working with the repo considerably gentler. ### Functionality retained @@ -70,7 +74,8 @@ Dotkernel Light is a stripped-down version of Dotkernel Frontend. Like Frontend, ## FAQ **Q: What is Dotkernel Light?** -A: Dotkernel Light is a version of Dotkernel Frontend that includes only the bare-bones essentials. It's suitable as a presentation site, an introduction to the Mezzio microframework architecture, or a starting point for a more complex project where you want full control over functionality. +A: Dotkernel Light is a version of Dotkernel Frontend that includes only the bare-bones essentials. +It's suitable as a presentation site, an introduction to the Mezzio microframework architecture, or a starting point for a more complex project where you want full control over functionality. **Q: What is the goal of Dotkernel Light?** A: It's designed to be a fast-start example of using the Mezzio microframework as well as an entry-level version of Dotkernel Frontend, presenting the beginner developer with as few moving parts as possible while still letting the more advanced developer have full control of the platform's functionality. diff --git a/public/readme/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md b/public/llms-content/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md similarity index 88% rename from public/readme/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md rename to public/llms-content/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md index d21b8efc..5241c021 100644 --- a/public/readme/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md +++ b/public/llms-content/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md @@ -12,7 +12,8 @@ language: "en" ## TL;DR -Dotkernel Light is a lightweight starting point for a project when you want full control over its functionality, and it grows into something more complex as you add packages. It comes with routing, templating, error handling, and tests/code quality checks out of the box, but strips out everything a presentation site doesn't need — database, sessions/cookies/flash messages, auth, dependency injection, mail, navigation, CORS, forms, the user/contact/plugin modules. +Dotkernel Light is a lightweight starting point for a project when you want full control over its functionality, and it grows into something more complex as you add packages. +It comes with routing, templating, error handling, and tests/code quality checks out of the box, but strips out everything a presentation site doesn't need — database, sessions/cookies/flash messages, auth, dependency injection, mail, navigation, CORS, forms, the user/contact/plugin modules. ## What's included vs. removed @@ -45,7 +46,8 @@ public function examplePageAction(): ResponseInterface The URL for this example page would be `/page/example-page`. -2. Create the matching template in `src/Page/templates/page/` — for the example above, `src/Page/templates/page/example-template.html.twig`. Put the page copy inside the `content` block: +2. Create the matching template in `src/Page/templates/page/` — for the example above, `src/Page/templates/page/example-template.html.twig`. +Put the page copy inside the `content` block: ```twig {% extends '@layout/default.html.twig' %} @@ -105,7 +107,8 @@ To promote pages on other platforms, edit the header section in `src/App/templat ### Top menu -This menu is displayed on all pages, in the header. Edit it in `src/App/templates/layout/default.html.twig`, under `id="navbarHeader"`: +This menu is displayed on all pages, in the header. +Edit it in `src/App/templates/layout/default.html.twig`, under `id="navbarHeader"`: ```html From d63f4ff8718083fbb0b4313a7c3749224d6bc823 Mon Sep 17 00:00:00 2001 From: OStefan2001 Date: Wed, 22 Jul 2026 19:00:38 +0300 Subject: [PATCH 3/4] cs-fix --- src/App/src/Fixture/AuthorLoader.php | 4 ++-- src/App/src/Fixture/PostLoader.php | 4 ++-- src/Blog/templates/page/JSON-LD/author-resource.jsonld.twig | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/App/src/Fixture/AuthorLoader.php b/src/App/src/Fixture/AuthorLoader.php index 2f9d93c7..ea874016 100644 --- a/src/App/src/Fixture/AuthorLoader.php +++ b/src/App/src/Fixture/AuthorLoader.php @@ -68,7 +68,7 @@ public function load(ObjectManager $manager): void $changed = true; } - echo ($changed ? "UPDATE: {$name}\n" : "UNCHANGED: {$name}\n"); + echo $changed ? "UPDATE: {$name}\n" : "UNCHANGED: {$name}\n"; } $this->addReference('author_' . $wpAuthorId, $author); @@ -89,4 +89,4 @@ public function getOrder(): int { return 1; } -} \ No newline at end of file +} diff --git a/src/App/src/Fixture/PostLoader.php b/src/App/src/Fixture/PostLoader.php index 9998ec12..fdfd49fc 100644 --- a/src/App/src/Fixture/PostLoader.php +++ b/src/App/src/Fixture/PostLoader.php @@ -117,7 +117,7 @@ public function load(ObjectManager $manager): void $changed = true; } - echo ($changed ? "UPDATE: {$title}\n" : "UNCHANGED: {$title}\n"); + echo $changed ? "UPDATE: {$title}\n" : "UNCHANGED: {$title}\n"; } } } @@ -144,4 +144,4 @@ public function getOrder(): int { return 3; } -} \ No newline at end of file +} diff --git a/src/Blog/templates/page/JSON-LD/author-resource.jsonld.twig b/src/Blog/templates/page/JSON-LD/author-resource.jsonld.twig index 3bfc20c4..cbec3e28 100644 --- a/src/Blog/templates/page/JSON-LD/author-resource.jsonld.twig +++ b/src/Blog/templates/page/JSON-LD/author-resource.jsonld.twig @@ -8,7 +8,7 @@ "mainEntity": { "@type": "Person", "name": "{{ author.name }}", - "github": "{{ author.github}}", + "github": "{{ author.github }}", "url": "{{ absolute_url(path('page::author-resource', {slug: author.slug})) }}" }, "hasPart": { From c197596bd5f022f5c4fab42be5e5e2ef861ab2fa Mon Sep 17 00:00:00 2001 From: OStefan2001 Date: Thu, 23 Jul 2026 10:28:40 +0300 Subject: [PATCH 4/4] Recommended changes to author and add migrations --- bin/doctrine-fixtures | 2 +- .../src/Migration/Version20260722123102.php | 35 +++++++++++++++++++ .../src/Migration/Version20260723072400.php | 31 ++++++++++++++++ src/Blog/src/Entity/Author.php | 2 +- .../templates/page/author-resource.html.twig | 2 +- 5 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/App/src/Migration/Version20260722123102.php create mode 100644 src/App/src/Migration/Version20260723072400.php diff --git a/bin/doctrine-fixtures b/bin/doctrine-fixtures index 0367d073..eece72e4 100644 --- a/bin/doctrine-fixtures +++ b/bin/doctrine-fixtures @@ -35,4 +35,4 @@ echo "Loading fixtures from: {$fixturesPath}\n"; $executor->execute($loader->getFixtures(), true); -echo "Fixtures loaded successfully!\n"; \ No newline at end of file +echo "Fixtures loaded successfully!\n"; diff --git a/src/App/src/Migration/Version20260722123102.php b/src/App/src/Migration/Version20260722123102.php new file mode 100644 index 00000000..08a78b8c --- /dev/null +++ b/src/App/src/Migration/Version20260722123102.php @@ -0,0 +1,35 @@ +addSql('DROP INDEX UNIQ_BDAFD8C8E7927C74 ON author'); + $this->addSql('ALTER TABLE author DROP email, CHANGE bio github LONGTEXT DEFAULT NULL'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_BDAFD8C8637CABE9 ON author (github)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX UNIQ_BDAFD8C8637CABE9 ON author'); + $this->addSql('ALTER TABLE author ADD email LONGTEXT NOT NULL, CHANGE github bio LONGTEXT DEFAULT NULL'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_BDAFD8C8E7927C74 ON author (email)'); + } +} diff --git a/src/App/src/Migration/Version20260723072400.php b/src/App/src/Migration/Version20260723072400.php new file mode 100644 index 00000000..868f4a81 --- /dev/null +++ b/src/App/src/Migration/Version20260723072400.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE author CHANGE github github VARCHAR(191) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE author CHANGE github github LONGTEXT DEFAULT NULL'); + } +} diff --git a/src/Blog/src/Entity/Author.php b/src/Blog/src/Entity/Author.php index 086d5093..2478d443 100644 --- a/src/Blog/src/Entity/Author.php +++ b/src/Blog/src/Entity/Author.php @@ -19,7 +19,7 @@ class Author extends AbstractEntity #[ORM\Column(name: 'slug', type: 'text', unique: true)] private string $slug; - #[ORM\Column(name: 'github', type: 'text', unique: true, nullable: true)] + #[ORM\Column(name: 'github', type: 'string', length: 191, unique: true, nullable: true)] private ?string $github = null; public function getName(): string diff --git a/src/Blog/templates/page/author-resource.html.twig b/src/Blog/templates/page/author-resource.html.twig index 49b498df..2f9fcdf6 100644 --- a/src/Blog/templates/page/author-resource.html.twig +++ b/src/Blog/templates/page/author-resource.html.twig @@ -19,7 +19,7 @@ - {% if author.github and author.github is not null %} + {% if author.github %}