From f2eed5fed824241600ff69d7dbea8d0261e7ef53 Mon Sep 17 00:00:00 2001 From: olaru Date: Tue, 8 Sep 2026 10:53:49 +0300 Subject: [PATCH] Rewrite .md files --- .../listen-for-android-install-referrer.md | 9 +- ...ers-in-the-same-app-for-the-same-action.md | 13 +- ...vider-bootstrap-modern-php-applications.md | 99 +++--- ...ifecycle-for-a-mezzio-based-application.md | 99 +++--- .../architecture/understanding-middleware.md | 87 +++--- .../best-practice/aptana-set-svn-keywords.md | 13 +- ...security-in-dotkernel-headless-platform.md | 25 +- ...golden-rules-of-professional-php-coding.md | 24 +- .../htaccess-301-redirect-non-www-to-www.md | 4 +- ...t-update-delete-statements-with-zend-db.md | 35 ++- .../sql-queries-using-zend-db-select.md | 46 +-- .../best-practice/subqueries-with-zend-db.md | 13 +- .../svn-export-in-a-virtual-host.md | 38 +-- ...n-keywords-setup-in-php-ide-zend-studio.md | 17 +- .../using-like-wildcards-with-zend-db.md | 47 +-- ...urning-the-fetch-functions-from-zend-db.md | 55 ++-- ...estamp-on-a-field-that-record-date-time.md | 9 +- ...inas-mvc-is-retiring-consider-it-solved.md | 25 +- ...r-15-handlers-in-dotkernel-applications.md | 28 +- ...-client-migration-from-postman-to-bruno.md | 121 ++++---- .../api-endpoint-to-collect-client-errors.md | 27 +- ...ntent-negotiation-in-dotkernel-rest-api.md | 111 +++---- .../dotkernel-api-1-0-0-released.md | 62 ++-- ...dotkernel-api-client-side-authorization.md | 22 +- ...dotkernel-api-server-side-authorization.md | 71 +++-- .../dotkernel-api-versus-laminas-api-tools.md | 28 +- ...ror-reporting-endpoint-in-dotkernel-api.md | 92 ++++-- ...to-implement-mailchimp-in-dotkernel-api.md | 39 ++- ...openapi-implementation-in-dotkernel-api.md | 118 +++++--- ...-cors-implementation-to-zend-expressive.md | 75 +++-- ...g-layer-to-wurfl-in-dotkernel-using-apc.md | 29 +- ...poser-support-in-your-dotkernel-project.md | 91 +++--- ...browser-detection-in-dotkernel-projects.md | 36 +-- ...n-using-cookie-remember-me-in-dotkernel.md | 95 +++--- ...through-bootstrap-of-non-existent-files.md | 15 +- ...ching-in-dotkernel-using-zend-framework.md | 48 ++- ...melcase-table-names-in-mysql-on-windows.md | 14 +- ...end-certified-engineers-zce-in-our-team.md | 12 +- .../configuring-the-cache-in-dotkernel.md | 37 +-- ...ade-easy-in-laminas-mezzio-applications.md | 58 ++-- ...cting-mobile-devices-in-dotkernel-1-6-0.md | 49 +-- ...able-wurfl-redirect-for-mobile-browsers.md | 13 +- ...ambiguation-dotkernel-1-and-dotkernel-3.md | 13 +- .../doctrine-cache-using-symfony-cache.md | 110 ++++--- ...ctrine-enum-implementation-in-dotkernel.md | 91 +++--- ...ts-and-services-north-american-relaunch.md | 19 +- .../dotkernel/dotkernel-1-2-0-release.md | 30 +- .../dotkernel/dotkernel-1-2-2-release.md | 33 +- .../dotkernel/dotkernel-1-3-0-release.md | 38 ++- .../dotkernel/dotkernel-1-3-2-release.md | 40 +-- .../dotkernel/dotkernel-1-5-0-released.md | 39 ++- .../dotkernel/dotkernel-1-8-0-lts-released.md | 65 ++-- ...ernel-1-8-1-upgrade-from-1-8-0-released.md | 21 +- .../dotkernel/dotkernel-coding-standard.md | 38 ++- ...l-database-naming-conventions-for-mysql.md | 35 ++- ...o-microframework-and-laminas-components.md | 49 ++- ...-best-choice-for-your-presentation-site.md | 133 ++++---- .../dotkernel/dotkernel-on-nginx.md | 75 +++-- ...nel-reserved-variable-names-for-caching.md | 64 ++-- .../dotkernel/dotkernel-template-engine.md | 6 +- .../dotkernel-version-1-0-in-action.md | 34 ++- ...-connections-and-character-set-in-mysql.md | 20 +- .../geoip-city-removed-from-dotkernel.md | 56 ++-- .../geoip-ip-address-location-in-dotkernel.md | 48 ++- ...rrors-with-dot-errorhandler-and-dot-log.md | 106 +++---- ...ghcharts-integration-in-dotkernel-1-6-0.md | 90 +++--- ...o-group-log-files-by-date-using-dot-log.md | 145 ++++----- ...ase-with-zend-framework-zend-db-adapter.md | 19 +- .../how-to-use-alerts-in-dotkernel.md | 82 +++-- ...d-hashing-api-from-php-5-5-in-dotkernel.md | 48 ++- ...extension-in-zend-server-5-6-on-windows.md | 69 +++-- ...p-extension-in-zend-server-6-on-windows.md | 24 +- ...ot-log-in-zend-expressive-and-dotkernel.md | 283 +++++------------- ...-upgrade-of-wurfl-xml-file-in-dotkernel.md | 28 +- ...ration-of-zend-framework-1-pear-channel.md | 39 ++- .../new-features-in-zend-framework-1-12.md | 65 ++-- ...tter-and-templates-for-zend-studio-10-1.md | 22 +- ...ing-admin-folder-with-htaccess-in-plesk.md | 24 +- ...as-mail-with-symfony-mailer-in-dot-mail.md | 99 +++--- ...urfl-cloud-php-library-to-dotkernel-1-6.md | 29 +- ...sing-dot-email-component-and-zend-email.md | 47 +-- .../dotkernel/templating-in-dotkernel3.md | 19 +- public/md-articles/dotkernel/test-article.md | 3 + ...ng-dotkernel-with-composer-dependencies.md | 90 +++--- .../using-utf8-charset-in-dotkernel.md | 33 +- ...fl-cloud-integration-in-dotkernel-1-6-0.md | 34 ++- ...api-license-incompatible-with-dotkernel.md | 41 ++- ...nd-framework-integration-into-dotkernel.md | 51 ++-- ...th-and-zend-acl-integrated-in-dotkernel.md | 47 +-- ...end-console-implementation-in-dotkernel.md | 25 +- ...rk-dropped-integration-of-wurfl-adapter.md | 16 +- .../zend-registry-usage-in-dotkernel.md | 46 +-- ...dotkernel-refactor-of-dot-session-class.md | 46 ++- ...tter-file-for-dotkernel-coding-standard.md | 13 +- .../development-report-december-11-2017.md | 19 +- .../dotkernel3/dotkernel-admin-v4.md | 75 ++--- .../dotkernel-admin-version-3-launched.md | 36 +-- ...tkernel-api-architecture-and-components.md | 157 ++++------ .../dotkernel-frontend-version-3-launched.md | 31 +- .../dotkernel3-stable-release-version-1-0.md | 29 +- .../php-8-3-support-in-dotkernel-admin.md | 116 +++---- .../php-8-3-support-in-dotkernel-api.md | 129 ++++---- .../php-8-3-support-in-dotkernel-frontend.md | 73 ++--- ...ry-admin-in-dotkernel-headless-platform.md | 84 +++--- ...the-root-of-dotkernel-headless-platform.md | 67 +++-- ...adless-platform-the-whats-hows-and-whys.md | 116 +++---- ...xecution-in-dotkernel-headless-platform.md | 129 ++++---- ...maker-generate-common-code-in-dotkernel.md | 102 ++++--- ...evolution-pattern-versus-api-versioning.md | 103 ++++--- ...sed-one-time-password-totp-in-dotkernel.md | 146 +++++---- ...ubmodule-in-dotkernel-headless-platform.md | 75 ++--- ...adds-postgresql-native-uuid-and-php-8-5.md | 19 +- ...cy-setup-in-dotkernel-using-mezzio-cors.md | 63 ++-- ...reating-admin-accounts-in-dotkernel-api.md | 117 +++----- ...database-migrations-and-how-to-use-them.md | 89 +++--- .../doctrine-cache-in-mezzio-and-dotkernel.md | 86 +++--- ...igration-without-dropping-custom-tables.md | 51 ++-- ...ly-url-in-an-generic-laminas-mezzio-app.md | 47 ++- ...in-wsl2-php-mariadb-composer-phpmyadmin.md | 57 ++-- ...ndpoints-in-dotkernel-api-using-dot-cli.md | 35 ++- ...-zend-expressive-2-to-zend-expressive-3.md | 88 ++---- ...nsole-with-dot-cli-based-on-laminas-cli.md | 69 +++-- ...an-for-documentation-in-dotkernel-api-3.md | 142 +++++---- ...sing-the-urlgenerator-work-in-fastroute.md | 49 ++- .../what-is-cross-origin-token-redemption.md | 31 +- .../how-to/what-is-psr-7-and-how-to-use-it.md | 113 ++++--- ...free-php-html-css-javascript-editor-ide.md | 50 ++-- .../md-articles/javascript/intro-to-jquery.md | 41 +-- .../javascript/javascript-email-validator.md | 10 +- ...-versus-lgpl-in-practice-dotkernel-case.md | 45 ++- ...provements-psr-15-handlers-vite-phpstan.md | 51 ++-- ...ic-routing-using-fastroute-in-dotkernel.md | 52 ++-- ...5-compliant-handlers-in-dotkernel-light.md | 86 +++--- ...-php-apache-mariadb-composer-phpmyadmin.md | 34 ++- .../aptana-php-installation-in-aptana-2-x.md | 5 +- ...er-unicode-support-in-mysql-5-5-utf8mb4.md | 23 +- ...seeding-doctrine-data-fixtures-vs-phinx.md | 99 +++--- .../end-of-support-for-php-5-2-x-branch.md | 20 +- ...oint-arithmetic-why-is-int-0-7-0-1-10-7.md | 98 +++--- .../how-to-upgrade-wamp-to-php-5-3-4.md | 77 ++--- .../mezzio-app-development-in-wsl2.md | 56 ++-- ...ased-no-upgrade-possible-for-wampserver.md | 16 +- ...ironment-development-staging-production.md | 28 +- .../php-support-back-in-aptana-3-0.md | 33 +- ...ion-using-pdo-and-zend-framework-part-2.md | 53 ++-- ...-injection-using-pdo-and-zend-framework.md | 62 +++- ...-to-mysql-server-on-plesk-based-servers.md | 27 +- ...c-analysis-replacing-psalm-with-phpstan.md | 35 ++- ...-to-connect-to-dotkernel-tracker-mantis.md | 66 ++-- .../using-php-7-express-in-zend-studio-13.md | 64 ++-- ...-control-ignore-patterns-in-zend-studio.md | 18 +- ...end-certified-engineer-in-dotboost-team.md | 13 +- .../zend-server-5-5-quick-setup-on-windows.md | 47 ++- ...x-installing-pear-packages-with-php-7-2.md | 51 ++-- ...s-the-intl-php-extension-problem-solved.md | 90 +++--- ...-quality-how-to-setup-phpcs-in-phpstorm.md | 68 ++--- ...debug-bar-a-very-helpfull-zf-debug-tool.md | 8 +- ...tting-pear-channel-for-zend-framework-1.md | 11 +- .../wurfl-php-api-libraries-gpl-versions.md | 7 +- ...ork-1-12-4-released-with-security-fixes.md | 9 +- .../zend-framework-1-7-0-released.md | 9 +- .../zend-framework-1-end-of-life.md | 23 +- ...r-accessible-repository-on-plesk-server.md | 47 ++- ...ecurity-fixes-in-zend-framework-1-12-12.md | 35 ++- 164 files changed, 4486 insertions(+), 4313 deletions(-) diff --git a/public/md-articles/android/listen-for-android-install-referrer.md b/public/md-articles/android/listen-for-android-install-referrer.md index 6aa70150..1f72e30d 100644 --- a/public/md-articles/android/listen-for-android-install-referrer.md +++ b/public/md-articles/android/listen-for-android-install-referrer.md @@ -12,14 +12,15 @@ language: "en" ## Getting Referrer Data at Install Time -Have you ever wondered if Android market sends you information at the moment of app install? Wouldn't it be nice to create custom links to your Android application, including bits of information about the referrer, and send it directly to the app for processing at install? This could be a simple and accurate solution for mobile app install tracking, but I'm sure you can find this useful in many ways. - -With Android, you actually get this information as a broadcasted intent by Android market at install time - even before opening your app... +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/md-articles/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md b/public/md-articles/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md index 7afd272e..692bc596 100644 --- a/public/md-articles/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md +++ b/public/md-articles/android/multiple-broadcast-receivers-in-the-same-app-for-the-same-action.md @@ -10,9 +10,14 @@ language: "en" # Multiple broadcast receivers in the same app, for the same action -Did you come to a point where using multiple broadcast receivers to listen for the same intent, separatly, in the same android app, leads to unexpected results? If that's the case, one broadcast receiver might consume the broadcasted intent, [online casino](http://www.cillap.com/) leaving the others with nothing to receive. This can be the case where you use 3rd party libraries with broadcast receivers defined. +## The problem -The following is a solution for this kind of problem, a code snippet inspired by the way Admob for android seems to solve this, as shown in their [documentation](http://developer.admob.com/wiki/Android_App_Download_Tracking), using meta-data in manifest file...[[read more](http://n3vrax.wordpress.com/2011/07/15/multiple-broadcast-receivers-in-the-same-app-for-the-same-action/)]. +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 + +A solution for this kind of problem is a code snippet inspired by the way Admob for Android solves this, as shown in Admob's own documentation, using meta-data in the manifest file. ## FAQ @@ -21,3 +26,7 @@ A: When multiple broadcast receivers are registered separately to listen for the **Q: When is this issue most likely to occur?** A: This can happen when you use 3rd party libraries that already define their own broadcast receivers alongside your app's own receivers. + +## Resources + +- [Admob App Download Tracking documentation](http://developer.admob.com/wiki/Android_App_Download_Tracking) diff --git a/public/md-articles/architecture/configprovider-bootstrap-modern-php-applications.md b/public/md-articles/architecture/configprovider-bootstrap-modern-php-applications.md index ecebcc44..992aca41 100644 --- a/public/md-articles/architecture/configprovider-bootstrap-modern-php-applications.md +++ b/public/md-articles/architecture/configprovider-bootstrap-modern-php-applications.md @@ -11,27 +11,25 @@ language: "en" # ConfigProvider - Bootstrap Modern PHP Applications ## 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, the `ConfigProvider` is a class that is part of an application's bootstrap process. **It's a class or callable that returns configuration data telling the platform which middleware should run, in what order, and sometimes under what conditions.** - -If you're talking specifically about the ConfigProvider in the Laminas/Mezzio ecosystem, it's literally an array of configuration, settings, or anything else your application needs. - ## Where Is the ConfigProvider Used? -Mezzio (formerly Zend Expressive), Laminas, Slim, the Dotkernel Headless Platform, or 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 pipeline configuration: - Middleware classes or service names. - Error-handling middleware, which should have the lowest priority. - Middleware groups or nested arrays. - Dependency injection mappings. - Request Handlers. -Example in Dotkernel, which is an approach similar to Laminas/Mezzio: +Example structure used in Dotkernel: -``` +```php class ConfigProvider { public function __invoke(): array @@ -44,46 +42,42 @@ class ConfigProvider public function getDependencies(): array { - return , - 'invokables' => , + return [ + 'factories' => [ /* ... */ ], + 'invokables' => [ /* ... */ ], ]; } public function getTemplates(): array { - return , - 'error' => , + return [ + 'paths' => [ /* ... */ ], + 'error' => [ /* ... */ ], ]; } } ``` -What each item above means: +What each item means: -- `dependencies` is used by the dependency injector (like [laminas-servicemanager](https://docs.mezzio.dev/mezzio/v3/features/container/laminas-servicemanager/)) to construct every requested service. - - `factories` will have the factory build the service. - - `invokables` will use `new` directly. - - You can also use `aliases` to redirect to another service name and `delegators` to wrap the original service. -- `templates` defines the paths for the template files. +| Item | Meaning | +|---|---| +| `dependencies` | Used by the dependency injector (e.g. [laminas-servicemanager](https://docs.mezzio.dev/mezzio/v3/features/container/laminas-servicemanager/)) to construct every requested service. | +| `factories` | The factory builds the service. | +| `invokables` | The service is built with `new` directly. | +| `aliases` | Redirects to another service name. | +| `delegators` | Wraps the original service. | +| `templates` | Defines the paths for the template files. | -## How the ConfigProvider works +## How the ConfigProvider Works -The ConfigProvider is automatically picked up by the framework during application bootstrap. Let's look at it step by step: +The ConfigProvider is automatically picked up by the framework during application bootstrap: -- **Merge the global configuration** - All ConfigProviders are merged into one array. -- **Read the configuration array** - The call is similar to the below and expects an array of entries: - -``` -$config = $container->get('config') ?? []; -``` - -- **Resolve item** - `$app->pipe()` is called to resolve one of the below instances: - - Resolve the service name from the container - - Wrap the middleware, if an array is provided - - Call the closure or invokable object. -- **Handle errors** - This middleware is the last one in the pipeline to make sure it handles any exceptions. -- **Execute at runtime** - [Laminas Stratigility](https://docs.laminas.dev/laminas-stratigility/) iterates over the pipeline in the order it was registered. - - Each middleware can **handle** the request and return a response, or **delegate** execution to the next middleware in the pipeline, until a `ResponseInterface` is returned to the client. +1. **Merge the global configuration** - All ConfigProviders are merged into one array. +2. **Read the configuration array** - A call similar to `$config = $container->get('config') ?? [];` reads an array of entries. +3. **Resolve item** - `$app->pipe()` is called to resolve one of the following: resolve the service name from the container, wrap the middleware if an array is provided, or call the closure or invokable object. +4. **Handle errors** - The error-handling middleware is the last one in the pipeline, to make sure it can handle any exceptions. +5. **Execute at runtime** - [Laminas Stratigility](https://docs.laminas.dev/laminas-stratigility/) iterates over the pipeline in the order it was registered. Each middleware can handle the request and return a response, or delegate execution to the next middleware in the pipeline, until a `ResponseInterface` is returned to the client. Below you can see how Mezzio and Dotkernel merge and use ConfigProviders to build the middleware pipeline and dependencies. @@ -91,22 +85,14 @@ Below you can see how Mezzio and Dotkernel merge and use ConfigProviders to buil ## Benefits -- Centralized setup – Instead of hardcoding bootstrap code, you declare it in a config provider so it's easy to read, change, or extend. -- Modular – Each package can ship with its own config without interfering with others. -- Container-friendly – It works well with frameworks using DI containers like Laminas ServiceManager, PHP-DI, or Pimple. -- Standardized service definitions - It has consistent rules for object creation that are separate from business logic. -- Auto-Discovery - In Laminas/Mezzio, the [ConfigAggregator](https://docs.laminas.dev/laminas-config-aggregator/) automatically loads and merges all ConfigProviders. - -> Dotkernel is an exception to this rule: 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 - It 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 the assertion of pipeline setup (e.g. check if a config key is present). - -## Additional Resources - -- [Mezzio Container](https://docs.mezzio.dev/mezzio/v3/features/container/config/) -- [Laminas Config Aggregator](https://docs.laminas.dev/laminas-config-aggregator/config-providers/) -- [PSR-15 (HTTP Server Request Handlers)](https://www.php-fig.org/psr/psr-15/) +- **Centralized setup** - Instead of hardcoding bootstrap code, it's declared in a config provider so it's easy to read, change, or extend. +- **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](https://docs.laminas.dev/laminas-config-aggregator/) 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). ## FAQ @@ -117,13 +103,20 @@ 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 (similar to $config = $container->get('config') ?? [];), 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. +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. **Q: Are new ConfigProviders auto-discovered in Dotkernel?** -A: Dotkernel is an exception to the usual auto-discovery rule: new ConfigProviders have to be added manually in config/config.php, because all the initial ConfigProviders required to install the applications are already injected. +A: Dotkernel is an exception to the usual auto-discovery rule: new ConfigProviders have to be added manually in `config/config.php`, because all the initial ConfigProviders required to install the applications are already injected. **Q: What are the benefits of using a ConfigProvider?** A: Benefits include centralized setup instead of hardcoded bootstrap code, modularity so each package can ship its own config, container-friendliness with DI containers like Laminas ServiceManager, PHP-DI or Pimple, standardized service definitions, environment-agnostic configuration for dev/test/prod, and better testability of the pipeline setup. + +## Resources + +- [Mezzio Container](https://docs.mezzio.dev/mezzio/v3/features/container/config/) +- [Laminas Config Aggregator](https://docs.laminas.dev/laminas-config-aggregator/config-providers/) +- [PSR-15 (HTTP Server Request Handlers)](https://www.php-fig.org/psr/psr-15/) diff --git a/public/md-articles/architecture/request-lifecycle-for-a-mezzio-based-application.md b/public/md-articles/architecture/request-lifecycle-for-a-mezzio-based-application.md index 889caffe..092d85f8 100644 --- a/public/md-articles/architecture/request-lifecycle-for-a-mezzio-based-application.md +++ b/public/md-articles/architecture/request-lifecycle-for-a-mezzio-based-application.md @@ -11,59 +11,45 @@ language: "en" # Request Lifecycle for a Mezzio-Based Application ## 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. - -## Seamlessly Interconnected Middleware for Enterprise-Level Solutions 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 graph below shows how the request is handled by **Dotkernel Light** ([GitHub](https://github.com/dotkernel/light), [documentation](https://docs.dotkernel.org/light-documentation/)), one of the applications in the [Dotkernel Headless Platform suite](https://github.com/dotkernel). - -> Hover over items for description - -Entry Point - -1. HTTP Request -[public/index.php] - -2. Service Container - -3. Route Registration - -4. Middleware Pipeline -[config/pipeline.php] - -5. Routing - -6. Handler -Invocation - -7. Custom Logic -Execution in Handler - -8. Template -Rendering [twig] - -9. Response -Creation - -10. Response -Pipeline - -asd - -asd - -asd - -11. Response Emitter - -HTTP 20x, 30x - -HTTP 40x - -HTTP 50x +## 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. + +### Processing + +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. + +### 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`. ## FAQ @@ -74,13 +60,20 @@ A: The request lifecycle is the sequence of steps that happen from the moment a A: The application bootstraps and loads configuration to create the Mezzio application instance, registers factories, aliases and delegators in the service container, reads all available routes with their allowed request methods and registers them (managed by FastRoute), and loads the predefined order of middleware in the pipeline. **Q: How does routing work in a Mezzio-based application?** -A: FastRoute matches the incoming URL and method against the registered routes, for example matching a GET request to /page/about against the GetPageViewHandler handler under the route name page::about. +A: FastRoute matches the incoming URL and method against the registered routes, for example matching a GET request to `/page/about` against the `GetPageViewHandler` handler under the route name `page::about`. **Q: What happens during handler invocation?** -A: The matched route name is extracted from the request attribute and passed to the renderer, using code similar to $template = $request->getAttribute(RouteResult::class)->getMatchedRouteName();, after which the handler executes the custom business logic. +A: The matched route name is extracted from the request attribute and passed to the renderer, using code similar to `$template = $request->getAttribute(RouteResult::class)->getMatchedRouteName();`, after which the handler executes the custom business logic. **Q: What happens during template rendering?** 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 + +- [Dotkernel Light on GitHub](https://github.com/dotkernel/light) +- [Dotkernel Light documentation](https://docs.dotkernel.org/light-documentation/) +- [Dotkernel Headless Platform suite on GitHub](https://github.com/dotkernel) diff --git a/public/md-articles/architecture/understanding-middleware.md b/public/md-articles/architecture/understanding-middleware.md index 1fd2a8d1..d2ccde1e 100644 --- a/public/md-articles/architecture/understanding-middleware.md +++ b/public/md-articles/architecture/understanding-middleware.md @@ -11,18 +11,18 @@ language: "en" # Understanding Middleware ## 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**, 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. - -## The purpose of middleware +## 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 +## Using Middleware -Middleware can be used to, but is not limited to, the following purposes: +Middleware can be used for purposes such as, but not limited to: - A/B Testing - Debugging @@ -36,21 +36,22 @@ Middleware can be used to, but is not limited to, the following purposes: ## Usage -According to [PSR-15: HTTP Server Request Handlers](https://www.php-fig.org/psr/psr-15/), 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](https://github.com/php-fig/http-server-middleware). +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 ``` -The middleware class must then implement the `process` method. +The middleware class must then implement the `process` method: -``` +```php public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface; ``` -Below is an example implementation of a middleware which processes the request. +Example implementation of a middleware which processes the request: -``` +```php class ExampleMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface @@ -62,9 +63,9 @@ class ExampleMiddleware implements MiddlewareInterface } ``` -This is an example implementation of a middleware which processes the response. +Example implementation of a middleware which processes the response: -``` +```php class ExampleMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface @@ -78,9 +79,9 @@ class ExampleMiddleware implements MiddlewareInterface } ``` -This approach processes both the request and response. +An approach that processes both the request and response: -``` +```php class ExampleMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface @@ -96,29 +97,24 @@ class ExampleMiddleware implements MiddlewareInterface } ``` -## How middleware is called +## 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 it **terminates the request** and returns a reponse. +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, the execution is eventually passed to your custom code which generates a response of its own. The execution then passes through the middleware in reverse order and returns the response. -- If the execution is terminated before reaching your custom code (e.g. via an exception), then the response is generated by the last middleware reached by the execution. +- 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 +## Middleware in Practice -A simple real world example of middleware usage can be the enhancement of a request with the user IP for logging porposes 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. The execution passes the control to the IP middleware which enhances the request with the user's IP and other relevant data. The control passes to your 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. - -## Additional resources: - -- [Why Care About PHP Middleware?](https://philsturgeon.uk/php/2016/05/31/why-care-about-php-middleware/) -- [Learn more about Mezzio from the source](https://docs.mezzio.dev/) -- [Laminas components](https://docs.laminas.dev/components/) -- [Dotkernel Light, the smallest complete Mezzio application](https://github.com/dotkernel/light) -- [The Slim PHP micro framework](https://www.slimframework.com/) -- [The PHP Framework Interop Group's full list of PSRs](https://www.php-fig.org/psr/) -- [PSR-7: The magical middleware tour](https://vimeo.com/showcase/4061778/video/177154167) -- [From Helpers to Middleware](https://www.youtube.com/watch?v=v1I57-_Rsv0) +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 @@ -126,16 +122,31 @@ The flow begins with a request. The execution passes the control to the IP middl 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. **Q: What interface must PHP middleware implement to be PSR-15 compliant?** -A: According to PSR-15, a compliant middleware must implement Psr\Http\Server\MiddlewareInterface, which requires a process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface method. +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 + +- [Why Care About PHP Middleware?](https://philsturgeon.uk/php/2016/05/31/why-care-about-php-middleware/) +- [Learn more about Mezzio from the source](https://docs.mezzio.dev/) +- [Laminas components](https://docs.laminas.dev/components/) +- [Dotkernel Light, an implementation of Mezzio using handlers](https://github.com/dotkernel/light) +- [The Slim PHP micro framework](https://www.slimframework.com/) +- [The PHP Framework Interop Group's full list of PSRs](https://www.php-fig.org/psr/) +- [PSR-7: The magical middleware tour](https://vimeo.com/showcase/4061778/video/177154167) +- [From Helpers to Middleware](https://www.youtube.com/watch?v=v1I57-_Rsv0) diff --git a/public/md-articles/best-practice/aptana-set-svn-keywords.md b/public/md-articles/best-practice/aptana-set-svn-keywords.md index 34501c99..f9e55a12 100644 --- a/public/md-articles/best-practice/aptana-set-svn-keywords.md +++ b/public/md-articles/best-practice/aptana-set-svn-keywords.md @@ -14,15 +14,15 @@ In Aptana it's very simple to set the [svn:keywords](http://svnbook.red-bean.com For example if you want to set the svn keyword property ***Id***: -- In the file where you want to add the svn keyword property write **$Id$** +1. In the file where you want to add the svn keyword property write **$Id$** ![](/uploads/article/019f8a80-cc86-73d9-a427-0621b2a55777/id-file-300x235.gif) -- Right click on the file, then follow Team -> Set Property...**Note***: *Set Property...* will not be active if you haven't first added the file to SVN: *Team*->*Add to Version Controller* +2. Right click on the file, then follow Team -> Set Property...**Note**: *Set Property...* will not be active if you haven't first added the file to SVN: *Team*->*Add to Version Controller* ![](/uploads/article/019f8a80-cc86-73d9-a427-0621b2a55777/set-property-300x152.gif) -- Select **svn:keywords**, and write **Id** in the text field  +3. Select **svn:keywords**, and write **Id** in the text field ![](/uploads/article/019f8a80-cc86-73d9-a427-0621b2a55777/svn-keywords-300x298.gif) @@ -36,7 +36,12 @@ When you make the SVN commit of the file, the *$Id$* keyword will be replaced wi 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. + +## Resources + +- [svn:keywords property documentation](http://svnbook.red-bean.com/en/1.4/svn.advanced.props.special.keywords.html) diff --git a/public/md-articles/best-practice/basic-security-in-dotkernel-headless-platform.md b/public/md-articles/best-practice/basic-security-in-dotkernel-headless-platform.md index 8dbb3310..a3ac45df 100644 --- a/public/md-articles/best-practice/basic-security-in-dotkernel-headless-platform.md +++ b/public/md-articles/best-practice/basic-security-in-dotkernel-headless-platform.md @@ -11,6 +11,7 @@ language: "en" # Basic Security in Dotkernel Headless Platform ## TL;DR + Software security should always be top of mind for a developer, since ignoring it can lead to major costs, data loss, GDPR fines, or the loss of client trust. The article surveys many facets of software security and walks through the practical measures Dotkernel Headless Platform takes for each: input validation, content negotiation, CORS, RBAC, demo credentials, error reporting, OpenAPI docs, PHP and JavaScript dependencies, OAuth2, session/cookie settings, and CI checks. **Software security** should always be in the back of your mind as a developer. It may seem fine at first to deliver a feature sooner, only to find later on that you left a backdoor into your crisp new update. You ignore security at your own risk, with potentially major costs to your personal or your organization's image, and to your client's trust in your abilities. The costs to recover the damages caused by a lacking security are sometimes astronomical, enough to put a company out of business. @@ -73,7 +74,7 @@ In Dotkernel API, CORS is handled by [mezzio/mezzio-cors](https://github.com/mez ## Role-Based Access Control -Role-Based Access Control (or RBAC) is a security model used in software systems to manage access to resource. It does this by assigning roles to users types which are in turn assigned to users who require a certain level of access. +Role-Based Access Control (or RBAC) is a security model used in software systems to manage access to resources. It does this by assigning roles to user types which are in turn assigned to users who require a certain level of access. Dotkernel API uses [mezzio/mezzio-authorization-rbac](https://github.com/mezzio/mezzio-authorization-rbac) for this purpose. There are several roles predefined, which you can configure to suit your project by editing the `config/autoload/authorization.global.php` file. @@ -87,7 +88,7 @@ Demo credentials are provided in Dotkernel API for your convenience, to allow yo The purpose for the error reporting endpoint is to have a reliable channel through which 3rd-party developers can report issues to you directly. -Dotkernel API has a dedicated endpoint `/error-report` for this purpose. It uses and `ErrorReportingToken` set up in the configuration file `config/autoload/error-handling.global.php`. +Dotkernel API has a dedicated endpoint `/error-report` for this purpose. It uses an `ErrorReportingToken` set up in the configuration file `config/autoload/error-handling.global.php`. ## OpenAPI Documentation @@ -99,7 +100,7 @@ OpenAPI documentation (formerly known as Swagger) provides a standardized, machi Dotkernel API implements [zircote/swagger-php](https://github.com/zircote/swagger-php) to provide an interactive documentation. -> Do **not** include sensitive information for you 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 @@ -116,7 +117,7 @@ OAuth 2.0 is a secure authorization framework that allows one application to acc Dotkernel API uses the [mezzio/mezzio-authentication-oauth2](https://github.com/mezzio/mezzio-authentication-oauth2) for the OAuth2 authentication service. The package itself is secure, but you still need to make sure you use it properly: - Replace or update the default `admin` and `frontend` clients on your production environment. -- Update the `access` and `refresh` tokens to match your aplication's requirements. The defaults are one day for `access` and one month for `refresh`. +- Update the `access` and `refresh` tokens to match your application's requirements. The defaults are one day for `access` and one month for `refresh`. - Make sure to **not** commit any local keys generated by `./vendor/bin/generate-oauth2-keys`. They are used to verify the transmitted JWTs. ## Session and Cookie Settings @@ -131,25 +132,26 @@ Dotkernel configures cookies in the `config/autoload/session.global.php` file. I ## JavaScript Dependencies -Very much like `composer` for PHP, JavaScript has its own dependencies, usually installed via `npm` or `yarn`. The JavaScript ecosystem has recently been attacked by hackers who targetted several widely used npm packages that have a total number of uses in the billions. +Very much like `composer` for PHP, JavaScript has its own dependencies, usually installed via `npm` or `yarn`. The JavaScript ecosystem has recently been attacked by hackers who targeted several widely used npm packages that have a total number of uses in the billions. Dotkernel uses `npm` to handle JavaScript dependencies. We monitor the news to stay on top of these security issues and use npm packages from reliable sources. Even so, you should regularly use the `npm audit` to check for vulnerabilities among your installed npm libraries. ## Other Security Considerations -All components of **Dotkernel Headless Platform** have several configuration files with the name format `*.global.php', '*.php.dist` and `*.local.php`. You must **only** include sensitive information in the `*.local.php` files, since they are, by default, ignored by the VCS. +All components of **Dotkernel Headless Platform** have several configuration files with the name format `*.global.php`, `*.php.dist` and `*.local.php`. You must **only** include sensitive information in the `*.local.php` files, since they are, by default, ignored by the VCS. The `development mode` is designed, as the name suggests, only for the development period. By enabling development mode, you enable features like debug mode, cache clear and show error details. These should be hidden from the production environment to avoid exposing sensitive data or code. The GitHub Action [Laminas Continuous Integration](https://github.com/laminas/laminas-continuous-integration-action) is an integral component of Dotkernel API. It ensures code quality by streamlining the execution of PHP quality assurance (QA) tasks within continuous integration (CI) workflows. Most often triggered by commits to the repository, it builds a matrix of tests: static analysis, coding standards checks, and unit tests. -## Additional Resources +## Resources - [Basic Security in Dotkernel Admin](https://docs.dotkernel.org/admin-documentation/v6/security/basic-security/) - [Basic Security in Dotkernel API](https://docs.dotkernel.org/api-documentation/v6/security/basic-security/) - [Content Negotiation in Dotkernel REST API](https://www.dotkernel.com/dotkernel-api/content-negotiation-in-dotkernel-rest-api/) - [laminas-form Documentation](https://docs.laminas.dev/laminas-form/v3/intro/) - [CORS in Dotkernel API](https://docs.dotkernel.org/api-documentation/v6/tutorials/cors/) +- [CORS Policy Setup in Dotkernel](https://www.dotkernel.com/how-to/mezzio-cors-implementation-in-dotkernel/) - [Error Reporting Endpoint](https://docs.dotkernel.org/api-documentation/v6/core-features/error-reporting/) - [OpenAPI Documentation](https://docs.dotkernel.org/api-documentation/v6/openapi/introduction/) - [mezzio/mezzio-authentication-oauth2 Configuration](https://docs.mezzio.dev/mezzio-authentication-oauth2/v1/intro/#configuration) @@ -160,13 +162,16 @@ The GitHub Action [Laminas Continuous Integration](https://github.com/laminas/la 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/md-articles/best-practice/golden-rules-of-professional-php-coding.md b/public/md-articles/best-practice/golden-rules-of-professional-php-coding.md index b22c70e0..a371ec2e 100644 --- a/public/md-articles/best-practice/golden-rules-of-professional-php-coding.md +++ b/public/md-articles/best-practice/golden-rules-of-professional-php-coding.md @@ -12,26 +12,26 @@ language: "en" 1.  Always use in development and in staging highest **error reporting** level, and **display_errors** ON: -``` +```php error_reporting(-1); ini_set('display_errors', 1); ``` -2. **Fix** every warning or notice that occur. 3. **Check** regularly server's error_log for notices/warnings +2. **Fix** every warning or notice that occurs. + +3. **Check** regularly server's error_log for notices/warnings 4. Identify any **temporary hack** with a special **mark**. Maybe something like: -``` +```php #@TODO masterpiece by @smartguy, to quick fix the division by zero ``` -  - -5. Each **function** must do a **single task**. If is log in the user and record the login in stats table, be nice and create a separate function for 'record the login' stuff. Maybe even a **distinct class** for stats ? +5. Each **function** must do a **single task**. If is log in the user and record the login in stats table, be nice and create a separate function for 'record the login' stuff. Maybe even a **distinct class** for stats? 6. Use a version control system. **SVN is NOT dead.** -7. Use an **[IDE](http://en.wikipedia.org/wiki/Integrated_development_environment)**.  [Aptana 2](http://www.aptana.com/products/studio2/download), [Aptana 3](http://www.aptana.com/products/studio3/download), Eclipse, even [Zend Studio](http://www.zend.com/en/products/studio/) . +7. Use an **[IDE](http://en.wikipedia.org/wiki/Integrated_development_environment)**. [Aptana 2](http://www.aptana.com/products/studio2/download), [Aptana 3](http://www.aptana.com/products/studio3/download), Eclipse, even [Zend Studio](http://www.zend.com/en/products/studio/). 8. Know your **IDE**: code snippets, code assist, integration with Zend Framework, SVN integration, bug tracker integration, and so on @@ -47,7 +47,15 @@ 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. + +## Resources + +- [Integrated development environment (Wikipedia)](http://en.wikipedia.org/wiki/Integrated_development_environment) +- [Aptana 2 download](http://www.aptana.com/products/studio2/download) +- [Aptana 3 download](http://www.aptana.com/products/studio3/download) +- [Zend Studio](http://www.zend.com/en/products/studio/) diff --git a/public/md-articles/best-practice/htaccess-301-redirect-non-www-to-www.md b/public/md-articles/best-practice/htaccess-301-redirect-non-www-to-www.md index 04d2c362..aa164852 100644 --- a/public/md-articles/best-practice/htaccess-301-redirect-non-www-to-www.md +++ b/public/md-articles/best-practice/htaccess-301-redirect-non-www-to-www.md @@ -12,14 +12,14 @@ language: "en" To always redirect users to the www site (for example: http://dotboost.com to http://www.dotboost.com), add the following lines to .htaccess, right after **RewriteEngine On**: -``` +```shell RewriteCond %{HTTP_HOST} ^dotboost.com RewriteRule ^(.*)$ http://www.dotboost.com/$1 [L,R=301] ``` If, on the other hand, you want to redirect http://www.dotboost.com to http://dotboost.com, add the following lines instead: -``` +```shell RewriteCond %{HTTP_HOST} ^www.dotboost.com RewriteRule ^(.*)$ http://dotboost.com/$1 [L,R=301] ``` diff --git a/public/md-articles/best-practice/insert-update-delete-statements-with-zend-db.md b/public/md-articles/best-practice/insert-update-delete-statements-with-zend-db.md index 4205a50f..79e45302 100644 --- a/public/md-articles/best-practice/insert-update-delete-statements-with-zend-db.md +++ b/public/md-articles/best-practice/insert-update-delete-statements-with-zend-db.md @@ -11,31 +11,30 @@ language: "en" # INSERT, UPDATE, DELETE statements with Zend_Db ## 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. Continuing the Zend_DB article [series](http://www.dotkernel.com/dotkernel/sql-queries-using-zend-db-select/), we are stopping now at DML statements. DML (Data Manipulation Language) statements are statements that change data values in database tables. There are 3 primary DML statements: - INSERT - Inserting new rows into database tables. -- UPDATE - Updating existing rows in database tables . +- UPDATE - Updating existing rows in database tables. - DELETE - Deleting existing rows from database tables. -*Note*:* - -``` +```php $db = Zend_Db::factory('Pdo_Mysql', $dbConnect); ``` **INSERT** -``` +```sql INSERT INTO user(email, password, firstName, lastName, active) VALUES ('$email', '$password', '$firstName', '$lastName', 1); ``` -The above SQL *INSERT* statement is translated in Zend_Db as follow: +The above SQL *INSERT* statement is translated in Zend_Db as follows: -``` +```php $data = array( 'email' => $email, 'password' => $password, 'firstName' => $firstName, @@ -46,7 +45,7 @@ $db->insert('user', $data); **UPDATE** -``` +```sql UPDATE user SET password = '$password', firstName = '$firstName', @@ -55,9 +54,9 @@ UPDATE user WHERE id = '$id' ``` -The above SQL *UPDATE* statemnet is translated in Zend_Db as follow: +The above SQL *UPDATE* statement is translated in Zend_Db as follows: -``` +```php $data = array('password' => $password, 'firstName' => $firstName, 'lastName' => $vlastname, @@ -67,26 +66,32 @@ $db->update('user', $data, 'id = '.$id); **DELETE** -``` +```sql DELETE FROM user WHERE id = '$id' ``` -The above SQL *DELETE* statemnet is translated in Zend_Db as follow: +The above SQL *DELETE* statement is translated in Zend_Db as follows: -``` +```php $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). **Q: How do you delete a row with Zend_Db?** A: Call $db->delete('user', 'id = '.$id), which is equivalent to the SQL statement DELETE FROM user WHERE id = '$id'. + +## Resources + +- [Zend_Db series](http://www.dotkernel.com/dotkernel/sql-select-zend-db/) diff --git a/public/md-articles/best-practice/sql-queries-using-zend-db-select.md b/public/md-articles/best-practice/sql-queries-using-zend-db-select.md index c20ad343..f68ff064 100644 --- a/public/md-articles/best-practice/sql-queries-using-zend-db-select.md +++ b/public/md-articles/best-practice/sql-queries-using-zend-db-select.md @@ -11,12 +11,13 @@ language: "en" # SQL queries using Zend_Db - SELECT ## 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](https://docs.laminas.dev/laminas-db/adapter/) and its related classes provide a simple SQL database interface for Zend Framework. To connect to MySql database, we are using Pdo_Mysql adapter : +[Zend_Db](https://docs.laminas.dev/laminas-db/adapter/) and its related classes provide a simple SQL database interface for Zend Framework. To connect to MySQL database, we are using Pdo_Mysql adapter : -``` +```php $db = Zend_Db::factory('Pdo_Mysql', $dbConnect); ``` @@ -24,22 +25,22 @@ $db = Zend_Db::factory('Pdo_Mysql', $dbConnect); The below 2 classical SQL queries are equivalent. First one is simple, the second one use INNER JOIN keyword, but the result is the same. -``` +```sql SELECT a.id, a.name, b.order_id FROM users AS a, orders AS b WHERE a.id = b.user_id AND a.id = {$userId} ``` -``` +```sql SELECT `a`.`id`, `a`.`name`, `b`.`order_id` FROM `users` AS `a` INNER JOIN `orders` AS `b` ON a.id = b.user_id WHERE (a.id = '{$userId}') ``` -The above querys are translated in Zend_Db style: +The above queries are translated in Zend_Db style: -``` +```php $select = $db->select() ->from(array('a'=>'users'), array('a.id', 'a.name')) @@ -49,15 +50,14 @@ $select = $db->select() If we don't want to select any column from the second table, the 3rd parameter of join() method should be an empty string -``` +```sql SELECT a.id, a.name FROM users AS a, orders AS b WHERE a.id = b.user_id AND a.id = {$userId} ``` -``` - > +```php $select = $db->select() ->from(array('a'=>'users'), array('a.id', 'a.name')) @@ -65,17 +65,16 @@ $select = $db->select() ->where('a.id = ?', $userId) ``` -Note*: If we don't write the 3rd parameter, it will select all the fields from that table: +Note: If we don't write the 3rd parameter, it will select all the fields from that table: -``` +```sql SELECT a.id, a.name, b.* FROM users AS a, orders AS b WHERE a.id = b.user_id AND a.id = {$user_id} ``` -``` - > +```php $select = $db->select() ->from(array('a'=>'users'), array('a.id', 'a.name')) @@ -85,31 +84,29 @@ $select = $db->select() **SELECT query - WHERE IN clause** -``` +```sql SELECT id FROM users WHERE aff_id IN ('1','2','3') ``` -``` - > +```php $select = $db->select() ->from('users', array('id')) ->where('aff_id IN (?)', array(1,2,3)); ``` -**Note*:** If you are not sure if you write the correct query, before you fetch it you can echo your query to visualize it: +**Note**: If you are not sure if you write the correct query, before you fetch it you can echo your query to visualize it: -``` +```php echo $select->__toString();exit; ``` -Also see: - [What are returning the FETCH functions from Zend_Db](http://www.dotkernel.com/best-practice/what-are-returning-the-fetch-functions-from-zend-db/) - [Subqueries with Zend_Db](http://www.dotkernel.com/best-practice/subqueries-with-zend-db/) - [INSERT, UPDATE, DELETE statements with Zend_Db](http://www.dotkernel.com/best-practice/insert-update-delete-statements-with-zend-db/) - ## 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. @@ -125,3 +122,10 @@ A: Use ->where('aff_id IN (?)', array(1,2,3)) on the select object, equivalent t **Q: How can you check that a Zend_Db select is generating the correct query?** A: Before fetching it, echo the query to visualize it: echo $select->__toString();exit; + +## Resources + +- [Zend_Db](https://docs.laminas.dev/laminas-db/adapter/) +- [What are returning the FETCH functions from Zend_Db](http://www.dotkernel.com/best-practice/what-are-returning-the-fetch-functions-from-zend-db/) +- [Subqueries with Zend_Db](http://www.dotkernel.com/best-practice/subqueries-with-zend-db/) +- [INSERT, UPDATE, DELETE statements with Zend_Db](http://www.dotkernel.com/best-practice/insert-update-delete-statements-with-zend-db/) diff --git a/public/md-articles/best-practice/subqueries-with-zend-db.md b/public/md-articles/best-practice/subqueries-with-zend-db.md index 3aefa608..f3a0f7ae 100644 --- a/public/md-articles/best-practice/subqueries-with-zend-db.md +++ b/public/md-articles/best-practice/subqueries-with-zend-db.md @@ -11,13 +11,14 @@ language: "en" # Subqueries with Zend_Db ## TL;DR + Continuing the Zend_Db series, this article shows a more complex query - combining COUNT(), LEFT JOIN, and GROUP BY across 3 tables, with a count taken from 2 different tables - and how to build it, including a nested subquery, using Zend_Db. Continuing the Zend_DB article [series](http://www.dotkernel.com/dotkernel/sql-queries-using-zend-db-select/), we are stopping now at subqueries. As you note, the below is a complicate query, with *COUNT()*, *LEFT JOIN()*, *GROUP BY* - select from 3 tables, and make a count from 2 different tables: -``` +```sql SELECT a.id, a.title, (SELECT COUNT(c.track_id) @@ -30,13 +31,13 @@ LEFT JOIN track_courses AS b ON (a.id = b.track_id) GROUP BY a.id ``` -Initialize the connection to our MySql database: +Initialize the connection to the MySQL database: -``` +```php $db = Zend_Db::factory('Pdo_Mysql', $dbConnect); ``` -``` +```php $db->select() ->from(array('a'=>'tracks'), array('id', @@ -65,3 +66,7 @@ A: Wrap a nested $db->select() call inside a Zend_Db_Expr, building the subquery **Q: How is the LEFT JOIN with a COUNT expressed in Zend_Db?** A: Use ->joinLeft(array('b'=>'track_courses'), 'a.id = b.track_id', array('count_courses' => 'COUNT(b.track_id)')) followed by ->group('a.id'). + +## Resources + +- [Zend_Db series](http://www.dotkernel.com/dotkernel/sql-select-zend-db/) diff --git a/public/md-articles/best-practice/svn-export-in-a-virtual-host.md b/public/md-articles/best-practice/svn-export-in-a-virtual-host.md index f7386f40..0286abf1 100644 --- a/public/md-articles/best-practice/svn-export-in-a-virtual-host.md +++ b/public/md-articles/best-practice/svn-export-in-a-virtual-host.md @@ -11,12 +11,13 @@ language: "en" # SVN Export in a virtual host ## 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. The following commands should be run in the terminal (for example, using Putty in Windows) on the host where you want to export the repository). It's recommended that you run them using the domain's user, not root. -First make sure that Subversion is installed on the host. To check if it is installed, run: +1. First make sure that Subversion is installed on the host. To check if it is installed, run: ``` svn --version @@ -24,44 +25,46 @@ svn --version If you don't get a "command not found" message, subversion is installed. Otherwise, you need to install it. -The next step is to go to where you want to export the contents of the repository (eg.: "*cd /var/www/vhosts/example.com/httpdocs*" or "*cd /home/sitename/public_html*"). +2. The next step is to go to where you want to export the contents of the repository (eg.: "*cd /var/www/vhosts/example.com/httpdocs*" or "*cd /home/sitename/public_html*"). -The command looks like this: +3. The command looks like this: -``` +```shell svn export repositoryUrl repositoryUrl ``` where: -- **-r revisionNumber** - *optional* - export a specific revision. By default, the latest revision will be used -- **repositoryUrl** - the repository URL (eg: *http://example.com/repos/project-name/trunk/*). Remember to add /trunk/, or change it appropriately if you need to export a certain branch or tag -- **targetDirectory** - - **./** - means the current directory - - **./project-name** - will export to the *project-name* subdirectory - - **/var/www/vhosts/example.com/httpdocs** - will export to an absolute path -- **--force** - *optional* - by default SVN will not export in an existing directory. if you want to override this, you have to use the *force* parameter. **Be careful, this option can overwrite files** +| Parameter | Meaning | +|---|---| +| `-r revisionNumber` | Optional. Exports a specific revision. By default, the latest revision is used. | +| `repositoryUrl` | The repository URL (e.g. `http://example.com/repos/project-name/trunk/`). Remember to add `/trunk/`, or change it appropriately for a branch or tag. | +| `targetDirectory` - `./` | The current directory. | +| `targetDirectory` - `./project-name` | Exports to the `project-name` subdirectory. | +| `targetDirectory` - `/var/www/vhosts/example.com/httpdocs` | Exports to an absolute path. | +| `--force` | Optional. By default SVN will not export into an existing directory; this overrides that. **Be careful, this option can overwrite files.** | + +4. For more information, you can run **svn help export**. Examples: -``` +```shell svn export http://v1.dotkernel.net/svn/trunk ./ --force svn export -r 423 http://v1.dotkernel.net/svn/trunk ./ --force svn export http://v1.dotkernel.net/svn/trunk /var/www/vhosts/domain.com/httpdocs/dk ``` -For more information, you can run **svn help export**. - If you've exported the repository using a different user (root for example), you can change the permissions back by running the following command as root: -``` +```shell 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. @@ -70,7 +73,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/md-articles/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md b/public/md-articles/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md index ec9e7cf5..3b39e348 100644 --- a/public/md-articles/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md +++ b/public/md-articles/best-practice/svn-keywords-setup-in-php-ide-zend-studio.md @@ -11,14 +11,17 @@ language: "en" # SVN keywords setup in PHP IDE ( Zend Studio) ## 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 a better integration of SVN, your PHP IDE( Zend Studio), and a bug tracker of choice, the below proprieties must be set, **for each project** you have. -Right click on **project** Go to **Team->Set Propriety** +1. Right click on **project**. + +2. Go to **Team->Set Propriety**. -- SVN Ignore files, below you have an example. As we do not want to commit your local settings to the main repository :-) +3. SVN Ignore files, below you have an example. As we do not want to commit your local settings to the main repository :-) ``` Name: svn:ignore @@ -32,7 +35,7 @@ cache *.ini ``` -- Basic integration with a bug tracker +4. Basic integration with a bug tracker ``` Name: bugtracq:label @@ -44,7 +47,7 @@ Name: bugtraq:message Propriety: [Tracker ID: #%BUGID%] ``` -- If you have a public bug tracker system , example Mantis +5. If you have a public bug tracker system, example Mantis ``` Name: bugtraq:url @@ -55,9 +58,9 @@ For **above** Proprieties , apply **only** to project folder, **NOT** recursive ## Final step( below instructions are good **only** for **svn:keywords** ) -  - -Check the **Apply property recursively to:** Select **All resources** Check the **Use filtration by the resource name** and add **Mask:** *.php +1. Check the **Apply property recursively to:**. +2. Select **All resources**. +3. Check the **Use filtration by the resource name** and add **Mask:** *.php. [![svn-add](/uploads/article/019f8a80-cc87-71b3-80b8-478826d88044/svn-add.jpg)](/uploads/2013/02/svn-add.jpg) diff --git a/public/md-articles/best-practice/using-like-wildcards-with-zend-db.md b/public/md-articles/best-practice/using-like-wildcards-with-zend-db.md index b89d59e3..6785a335 100644 --- a/public/md-articles/best-practice/using-like-wildcards-with-zend-db.md +++ b/public/md-articles/best-practice/using-like-wildcards-with-zend-db.md @@ -11,6 +11,7 @@ language: "en" # Using LIKE wildcards with Zend_Db ## 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. @@ -24,9 +25,7 @@ The **LIKE** condition allows you to use wildcards in the *WHERE* clause of an S - ***_*** allows you to match a single character - ***%*** allows you to match any string of any length (including zero length) -*Note*:* - -``` +```php $db = Zend_Db::factory('Pdo_Mysql', $dbConnect); ``` @@ -34,11 +33,11 @@ $db = Zend_Db::factory('Pdo_Mysql', $dbConnect); - Return all ids which start with '1' and second digit is between 0 and 9 (10, 11, 12, ..., 18, 19): -``` +```sql SELECT * FROM `table` WHERE (`id` LIKE '1_' ) ``` -``` +```php $col = $this->db->quoteIdentifier('id'); $where = $this->db->quoteInto("$col LIKE ? ", '1_'); $select = $this->db->select() @@ -47,13 +46,13 @@ $select = $this->db->select() $result = $this->db->fetchAll($select); ``` -- Return all instances whose name is 4 characters long, where the first two characters are 'Fr' and the last character is 'd' (Frad, Fred, Frod, etc.) : +- Return all instances whose name is 4 characters long, where the first two characters are 'Fr' and the last character is 'd' (Frad, Fred, Frod, etc.): -``` +```sql SELECT * FROM `table` WHERE (`name` LIKE 'Fr_d' ) ``` -``` +```php $col = $this->db->quoteIdentifier('name'); $where = $this->db->quoteInto("$col LIKE ? ", 'Fr_d'); $select = $this->db->select() @@ -66,11 +65,11 @@ $result = $this->db->fetchAll($select); - Returns all instances that have the 'gallery' string in the *source* field: -``` +```sql SELECT * FROM `table` WHERE (`source` LIKE '%gallery%' ) ``` -``` +```php $col = $this->db->quoteIdentifier('source'); $where = $this->db->quoteInto("$col LIKE ? ", '%gallery%'); $select = $this->db->select() @@ -81,11 +80,11 @@ $result = $this->db->fetchAll($select); - Returns all instances that have the 'gallery' or 'folder' strings in the *source* field: -``` +```sql SELECT * FROM `table` WHERE (`source` LIKE '%gallery%' OR `source` LIKE ('%folder%') ) ``` -``` +```php $col = $this->db->quoteIdentifier('source'); $where = $this->db->quoteInto("$col LIKE ? ", '%gallery%'); $where .= $this->db->quoteInto("OR $col LIKE (?) ", '%folder%'); @@ -97,13 +96,13 @@ $result = $this->db->fetchAll($select); **NOT LIKE _** -- Returns all 2-digit ids that don't start with *1* (20->99 ) or have a different number of digits than 2 (1, 2, ..., 8, 9, 100, 101, ...): +- Returns all 2-digit ids that don't start with *1* (20->99) or have a different number of digits than 2 (1, 2, ..., 8, 9, 100, 101, ...): -``` +```sql SELECT * FROM `table` WHERE (`id` NOT LIKE '1_' ) ``` -``` +```php $col = $this->db->quoteIdentifier('id'); $where = $this->db->quoteInto("$col NOT LIKE ? ", '1_'); $select = $this->db->select() @@ -116,11 +115,11 @@ $result = $this->db->fetchAll($select); - Returns all instances that don't have 'gallery', 'folder' or 'file' strings in the *source* field: -``` +```sql SELECT * FROM `table` WHERE (`source` NOT LIKE ('%gallery%') AND `source` NOT LIKE ('%folder%') AND `source` NOT LIKE ('%file%') ) ``` -``` +```php $col = $this->db->quoteIdentifier('source'); $where = $this->db->quoteInto("$col NOT LIKE (?) ", '%gallery%'); $where .= $this->db->quoteInto("AND $col NOT LIKE (?) ", '%folder%'); @@ -131,13 +130,13 @@ $select = $this->db->select() $result = $this->db->fetchAll($select); ``` -**OTHER Example** +**Other example** -``` -SELECT * FROM `table` WHERE `number` LIKE '_6%' +```sql +SELECT * FROM `table` WHERE `number` LIKE '_6%' ``` -``` +```php $col = $this->db->quoteIdentifier('number'); $where = $this->db->quoteInto("$col LIKE ? ", '_6%'); $select = $this->db->select() @@ -160,10 +159,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/md-articles/best-practice/what-are-returning-the-fetch-functions-from-zend-db.md b/public/md-articles/best-practice/what-are-returning-the-fetch-functions-from-zend-db.md index 020c45aa..5fcb03d3 100644 --- a/public/md-articles/best-practice/what-are-returning-the-fetch-functions-from-zend-db.md +++ b/public/md-articles/best-practice/what-are-returning-the-fetch-functions-from-zend-db.md @@ -11,45 +11,50 @@ 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. Continuing the Zend_DB article [series](http://www.dotkernel.com/dotkernel/sql-queries-using-zend-db-select/), we are stopping now at *FETCH* methods that are in [Zend_Db_Adapter_Abstract](https://docs.laminas.dev/laminas-db/adapter/): -``` -array fetchAll (string|Zend_Db_Select $sql, , ) -array fetchAssoc (string|Zend_Db_Select $sql, [mixed $bind = array()]) -array fetchCol (string|Zend_Db_Select $sql, [mixed $bind = array()]) -string fetchOne (string|Zend_Db_Select $sql, [mixed $bind = array()]) -array fetchPairs (string|Zend_Db_Select $sql, [mixed $bind = array()]) -array fetchRow (string|Zend_Db_Select $sql, , ) +```php +array fetchAll (string|Zend_Db_Select $sql, [mixed $bind = array()]) +array fetchAssoc (string|Zend_Db_Select $sql, [mixed $bind = array()]) +array fetchCol (string|Zend_Db_Select $sql, [mixed $bind = array()]) +string fetchOne (string|Zend_Db_Select $sql, [mixed $bind = array()]) +array fetchPairs (string|Zend_Db_Select $sql, [mixed $bind = array()]) +array fetchRow (string|Zend_Db_Select $sql, [mixed $bind = array()]) ``` To be more easily to follow, in green box is the classical SQL statement, and in blue box is the query written in Zend_Db style. -Lets start. Initialize the connection to our MySql database: +Initialize the connection to the MySQL database: -``` +```php $db = Zend_Db::factory('Pdo_Mysql', $dbConnect); ``` -Here is a SQL query, that we want to fetch: +Here is a SQL query that we want to fetch: -``` +```sql $sql = "SELECT id, title FROM files"; $db->query($sql) ``` -``` +```php $select = $db->select() ->from('files', array('id', 'title')) ``` -*Note*:* for the old style of fetching we used an old class. What you need to know is: - *query()* method is similar with mysqli_query() from *Mysqli* PHP extension - *next_record()* method is similar with mysqli_next_result() from *Mysqli* PHP extension - *f()* method retrieve the value of the column specified as parameter +Note: for the old style of fetching we used an old class. What you need to know is: + +- *query()* method is similar with mysqli_query() from *Mysqli* PHP extension +- *next_record()* method is similar with mysqli_next_result() from *Mysqli* PHP extension +- *f()* method retrieve the value of the column specified as parameter **fetchAll** -``` +```php while($db->next_record()) { $a[] = array( @@ -59,13 +64,13 @@ while($db->next_record()) } ``` -``` +```php $a = $db->fetchAll($select); ``` **fetchAssoc** -``` +```php while($db->next_record()) { $a[$db->f('id')] = array( @@ -75,50 +80,50 @@ while($db->next_record()) } ``` -``` +```php $a = $db->fetchAssoc($select); ``` **fetchCol** -``` +```php while($db->next_record()) { $a[] = $db->f('id'); } ``` -``` +```php $a = $db->fetchCol($select); ``` **fetchOne** -``` +```php $db->next_record(); $a = $db->f('id'); ``` -``` +```php $a = $db->fetchOne($select); ``` **fetchPairs** -``` +```php while($db->next_record()) { $a[$db->f('id')] = $db->f('title'); } ``` -``` +```php $a = $db->fetchPairs($select); ``` **fetchRow** -``` +```php $db->next_record(); $a = array( 'id' => $db->f('id'), @@ -126,7 +131,7 @@ $a = array( ); ``` -``` +```php $a = $db->fetchRow($select); ``` diff --git a/public/md-articles/best-practice/why-use-current-timestamp-on-a-field-that-record-date-time.md b/public/md-articles/best-practice/why-use-current-timestamp-on-a-field-that-record-date-time.md index 1f9c909e..07110f6d 100644 --- a/public/md-articles/best-practice/why-use-current-timestamp-on-a-field-that-record-date-time.md +++ b/public/md-articles/best-practice/why-use-current-timestamp-on-a-field-that-record-date-time.md @@ -11,19 +11,20 @@ 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. On a *TIMESTAMP field* that records date and time when *inserting* a new record, it is encouraged to use as a *DEFAULT* value, the **CURRENT_TIMESTAMP** constant. **Why?** Because when inserting a new row in the table for the date and time field there is no need to specifically add its value, either by creating it from PHP code with the [Date/ Time functions](http://www.php.net/manual/en/ref.datetime.php) or with MySQL function [NOW()](http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_now) -``` +```sql ALTER TABLE `user` CHANGE `dateCreated` `dateCreated` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; ``` -CURRENT_TIMESTAMP is also a solution for  *updating* date and time fields. Use *`ON UPDATE CURRENT_TIMESTAMP`* clause, if you want the value of the field to be changed automatically each time the row is updated. +CURRENT_TIMESTAMP is also a solution for *updating* date and time fields. Use *`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; ``` @@ -37,7 +38,7 @@ ALTER TABLE `user` CHANGE `dateLogin` `dateLogin` TIMESTAMP ON UPDATE CURRENT_TI For more details check out [MySQL Manual](https://dev.mysql.com/doc/refman/9.7/en/datetime.html) -**Note*:** Only one timestamp field can be `DEFAULT CURRENT_TIMESTAMP` in a table. +**Note**: Only one timestamp field can be `DEFAULT CURRENT_TIMESTAMP` in a table. ## FAQ diff --git a/public/md-articles/best-practice/zf-is-retired-laminas-mvc-is-retiring-consider-it-solved.md b/public/md-articles/best-practice/zf-is-retired-laminas-mvc-is-retiring-consider-it-solved.md index 58439378..fbcfd46f 100644 --- a/public/md-articles/best-practice/zf-is-retired-laminas-mvc-is-retiring-consider-it-solved.md +++ b/public/md-articles/best-practice/zf-is-retired-laminas-mvc-is-retiring-consider-it-solved.md @@ -11,6 +11,7 @@ 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. @@ -22,13 +23,13 @@ Laminas MVC is not even the first framework that has reached its end of life - l ## 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 viablity of **migrating to a newer platform**. +**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 to 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. +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 @@ -41,9 +42,9 @@ The MVC architecture is obsolete. It is yesterday's architecture, fit for monoli | 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 +## How Apidemia Handles Migrations -Apidemia have 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. +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**: @@ -53,14 +54,6 @@ This is the simplified **task list**: - **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. -## Additional 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/) - ## FAQ **Q: What is Laminas MVC being replaced by?** @@ -77,3 +70,11 @@ A: A simplified task list: code audit & migration strategy; partial or full migr **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/md-articles/design-pattern/naming-pattern-for-psr-15-handlers-in-dotkernel-applications.md b/public/md-articles/design-pattern/naming-pattern-for-psr-15-handlers-in-dotkernel-applications.md index 8b42191f..1c90737d 100644 --- a/public/md-articles/design-pattern/naming-pattern-for-psr-15-handlers-in-dotkernel-applications.md +++ b/public/md-articles/design-pattern/naming-pattern-for-psr-15-handlers-in-dotkernel-applications.md @@ -31,12 +31,12 @@ Naming patterns can be defined for different types of files, since `image` files Here is a list of items to consider: - The file names should be kept as **short** as possible, while retaining relevant items to help outline the file's purpose. -- **Abreviations** are ok to use, but special characters should be avoided, excepting dash and underscore which are fine, no matter the operating system you use. +- **Abbreviations** are ok to use, but special characters should be avoided, excepting dash and underscore which are fine, no matter the operating system you use. - **Versioning and metadata** can also help visually. - Grouping files into **folders** is also recommended, especially when you are dealing with files that are related. - If **category names** are relevant to use, you can standardize their names by using a shortened version, maybe with 2-3 letters. -After defining your naming pattern, the most import item by far is to **communicate the pattern to the team**. A top-level README file with the documentation should be kept handy for any developer who creates new files. +After defining your naming pattern, the most important item by far is to **communicate the pattern to the team**. A top-level README file with the documentation should be kept handy for any developer who creates new files. ## The naming pattern for Dotkernel Handlers @@ -45,7 +45,7 @@ HTTP request handlers are at the core of any web application. They receive a req Even the first paragraph above mentions several elements that are relevant. The naming pattern for our `Handlers` contains: - The **method** or verb used by the handler (e.g. GET, POST). -- The **resouce** name (e.g. Admin, Account). +- The **resource** name (e.g. Admin, Account). - The performed **action** (e.g. CreateForm, List). - An optional **Form** if the handler returns a form that will perform another action when submitted. - The string **Handler**. @@ -75,8 +75,24 @@ You will likely need to update and delete products further down the line, so you It takes only a minute to build the proper name for each handler, which takes you and other team members no more than a second to figure out what it does. You will thank yourself in the future. -## Additional resources +## FAQ -[PSR-15](https://www.php-fig.org/psr/psr-15/) +**Q: What is a naming pattern?** +A: A naming pattern helps you organize and quickly identify your files by using relevant strings in file names, such as what a file refers to, the action it performs, how it relates to other files, its author, and its creation date. -[Dotkernel Application Repositories](https://github.com/dotkernel) +**Q: What elements make up the naming pattern for Dotkernel Handlers?** +A: The method or verb used by the handler (e.g. GET, POST), the resource name (e.g. Admin, Account), the performed action (e.g. CreateForm, List), an optional Form suffix, and the string Handler. + +**Q: What wording is used for the performed actions (CRUD)?** +A: Create, Get for Read, Edit for Update, and Delete. + +**Q: Where is this naming pattern used?** +A: It is used in Dotkernel Admin v6 and will also be implemented in the next releases for Frontend and Light. + +**Q: What is a practical example of this naming pattern?** +A: For a product resource managed by admin users, you would get handlers such as `GetProductCreateFormHandler`, `PostProductCreateHandler`, `GetProductEditFormHandler`, `PostProductEditHandler`, `PostProductDeleteHandler`, and `GetProductListHandler`. + +## Resources + +- [PSR-15](https://www.php-fig.org/psr/psr-15/) +- [Dotkernel Application Repositories](https://github.com/dotkernel) diff --git a/public/md-articles/dotkernel-api/api-client-migration-from-postman-to-bruno.md b/public/md-articles/dotkernel-api/api-client-migration-from-postman-to-bruno.md index c19a0604..d4a42910 100644 --- a/public/md-articles/dotkernel-api/api-client-migration-from-postman-to-bruno.md +++ b/public/md-articles/dotkernel-api/api-client-migration-from-postman-to-bruno.md @@ -11,111 +11,90 @@ language: "en" # API Client Migration: From Postman to Bruno ## 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. ## Why We Switched to the Offline-Focused Bruno -Every API developer knows that to build an API properly **you need a reliable client for testing and interacting with the API**. Ideally this tool should be free, it should store endpoint collections and share them easily with your team, and it should be fast and secure. - -Over the years we have been using Postman as our go-to API tester, but recently we have considered **shifting to Bruno**, a lightweight alternative. We are not the first to consider this change. There is 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 -![](/uploads/article/019f8a80-cc8f-729f-99cd-7e0d87163867/bruno-vs-postman1-1024x683.jpg) - -### Architecture - -Primarily, we use collections to save our API endpoints and then share them among our developers to streamline testing. Postman has recently decided to only allow **one user account in their free plan**. In some cases, it might be enough, but this is one reason why Postman is less reliable right now. - -Bruno comes with a different approach: **a fully-offline experience** based on shared `.bru` files. Being that it's offline means that there is **no restriction on the number of developers** using Bruno's files, so it's an advantage for the way we do things. - -### Version Control - -Postman focuses on storing collections and handling version control in the cloud, **forcing developers to always be online**. You can still export/import collections via their UI. - -Bruno's `.bru` **files can be saved in the Git repository** and easily accessed by all members of the dev team. Version control for Bruno files is handled entirely via Git, just like for any other file on your project. - -### Feature Scope - -A major difference between Postman and Bruno is the feature scope. Where Postman offers a **complete platform for the API lifecycle**, Bruno focuses mainly on **interacting with the API, writing simple tests, and building local collections**. - -Postman is the definite winner in this department, if you also use features like mocking, documentation, and CI/CD integration. Given that we use different tool for these additional features, Postman doesn't really benefit us compared to Bruno. - -### Performance - -Bruno is the clear winner here, given that it **uses much less RAM** and is **generally faster**. +| Aspect | Postman | Bruno | +|---|---|---| +| Architecture | Free plan limited to one user account | Fully-offline experience via shared `.bru` files; no restriction on number of developers | +| Version control | Handled in the cloud; requires being online (export/import via UI possible) | `.bru` files saved directly in the Git repository, versioned via Git like any other file | +| Feature scope | Complete platform for the API lifecycle (mocking, documentation, CI/CD integration) | Focused mainly on interacting with the API, writing simple tests, and building local collections | +| Performance | Needs to regularly sync with the cloud and store advanced features in RAM, which can introduce delays | Uses much less RAM and is generally faster | +| Security | Offers Single Sign-On (SSO) and Role-Based Access Control (RBAC) | Local files never leave the dev environment, arguably more secure | +| Collection sharing | Limited sharing for multi-member dev teams | Can share via Git, `.zip` file, or a single `.yaml` file; Git is the preferred option | -Postman needs to regularly synchronize with the cloud and store its advanced features in the RAM, which can introduce delays in some cases. +### Comparison Conclusion -### Security +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 offers **Single Sign-On (SSO) and Role-Based Access Control (RBAC)**, but we find these not to be useful for our workflow. +## Alternative API Clients -Bruno's local files do offer a distinct advantage in that these **files never leave your dev environment**. It can be argued that it's **more secure** this way, especially since we try to not share our client's files with any online tool if we can help it. +Bruno is only one of several alternatives to Postman: -### Working with Bruno Collections +- Hoppscotch - runs in the browser or as a PWA +- Insomnia - clear UI and large plugin ecosystem +- HTTPie - focuses on terminal-based workflows +- Thunder Client - built into Visual Studio Code +- Apidog - covers the whole API lifecycle +- Yaak - minimal and fast desktop client -One of the most important functions that Postman has limited right now is the ability to **share collection with dev team made up of multiple members**. Bruno offers several options, like saving a collection to a Git, to a `.zip` file or a single `.yaml` file. By far the best option is sharing via Git which allows a single or multiple developers to quickly sync between multiple work locations, to track change history, and to easily publish their work for public consumption. - -We intend to **create a separate Git repository within each project for the Bruno files** to streamline the sharing process. For each new proejct, we normally allocate multiple developers from the get-to: one for frontend, another for backend. But the teams often grow as the project becomes more complex. Developers may be reallocated, so being able to swiftly onboard a new member to the team is vital. - -## Comparison Conclusion - -Postman is currently a better fit for larger teams that are willing to allocate a budget for their more feature-rich platform. Bruno stores collections in Git, so everything is offline, and thus as far as we are concerned it's more secure, while also being generally faster. - -## Alternative API clients - -Bruno is only one of the alternatives to Postman. Let's see some of the other API clients available on the market right now: - -- [Hoppscotch](https://hoppscotch.io/) runs in the browser or as a PWA (Progressive Web App that offers a native app-like experience). -- [Insomnia](https://insomnia.rest/) with its clear UI and large plugin ecosystem. -- [HTTPie](https://httpie.io/) focuses on terminal-based workflows. -- [Thunder Client](https://www.thunderclient.com/) which is built into Visual Studio Code. -- [Apidog](https://apidog.com/) covers the whole API lifecycle. -- [Yaak](https://yaak.app/) with its minimal and fast desktop client. - -As far as we are concerned, any one of them can get the job done. The decision comes down to choosing a simple, reliable tool we can use for the forseeable future. +Any of them can get the job done; the decision comes down to choosing a simple, reliable tool for the foreseeable future. ## Bruno for Dotkernel -At the moment, Bruno seems to be the best match for us. It offers similar functionality to Postman, with the added benefits of: - -- Allowing us to work completely offline. -- Saving the enpoint collection to our GitHub accounts. - -The similar functionality is to be expected, since it's an API client, first and foremost. The offline feature is perhaps what weighed the most in our decision in favor of Bruno. +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 -Most of us have only worked with Postman, so switching to another tool can impact efficiency, at least at first. In general, Tool Migration can have an emotional impact on developers who use a tool, because they have to learn the new tool's ins and outs before getting back to the real work. - -Given Bruno's straightforward approach and reasonable learning curve, this should be mitigated easily within our company. In fact, we see the switch as an **expansion of our expertise**. We thus prevent getting tied up to one tool, something similar to vendor lock-in for code. +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? -We fully expect Bruno to eventually restrict developers with paid plans, just like Postman did, but we'll cross that bridge when we get to it. Hopefully, we won't have to develop our own API client (fingers crossed). For now, **Bruno becomes our de facto API client** and will encourage our whole team to adopt it as soon as possible. - -## Additional Resources - -[Bruno homepage](https://www.usebruno.com/) +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. +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 + +- [Bruno homepage](https://www.usebruno.com/) +- [Hoppscotch](https://hoppscotch.io/) +- [Insomnia](https://insomnia.rest/) +- [HTTPie](https://httpie.io/) +- [Thunder Client](https://www.thunderclient.com/) +- [Apidog](https://apidog.com/) +- [Yaak](https://yaak.app/) diff --git a/public/md-articles/dotkernel-api/api-endpoint-to-collect-client-errors.md b/public/md-articles/dotkernel-api/api-endpoint-to-collect-client-errors.md index f747529d..53cc2060 100644 --- a/public/md-articles/dotkernel-api/api-endpoint-to-collect-client-errors.md +++ b/public/md-articles/dotkernel-api/api-endpoint-to-collect-client-errors.md @@ -10,25 +10,26 @@ language: "en" # API Endpoint to Collect Client Errors -## 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. -Let's say you have a **(Client)** **Frontend** (e.g. Angular) over a [Dotkernel API](https://github.com/dotkernel/api) and there may be cases when there are errors, eighter the API changed it's response(s) over night or just a simple variable being `undefined` for some reason. +## Usage -Since in our case **Frontend** is running on user client there is so little to do but we've come with an ideea for "writing down" any inconveniences. +Send a POST request to your Dotkernel API on the route: -We have created an **endpoint** where **Clients** can submit the **error message** when things are going down hill. - -A simple **POST** to your **Dotkernel API** on route: `https://api.dotkernel.net/error-report` +``` +https://api.dotkernel.net/error-report +``` -With body: +With a body: -``` +```shell { "message": "My awesome error!!!" } ``` -Note: The error **message** will be stored by default in `/log/error-report-endpoint-log.log`, a separate log for **Client** and the message will be saved with a **timestamp**. +Note: the error message is stored by default in `/log/error-report-endpoint-log.log`, a separate log for Client, and the message is saved together with a timestamp. ## FAQ @@ -36,7 +37,11 @@ Note: The error **message** will be stored by default in `/log/error-report-endp A: When a Frontend client (e.g. Angular) running on the user's machine hits an error against the Dotkernel API - whether from an overnight API response change or a simple undefined variable - there is little that can be done from the client side, so this endpoint lets clients "write down" the error instead. **Q: How do I submit an error from the client?** -A: Send a simple POST request to your Dotkernel API's https://api.dotkernel.net/error-report route, with a body such as { "message": "My awesome error!!!" }. +A: Send a simple POST request to your Dotkernel API's `https://api.dotkernel.net/error-report` route, with a body such as `{ "message": "My awesome error!!!" }`. **Q: Where is the submitted error message stored?** -A: By default, it is stored in a separate log file for Client, /log/error-report-endpoint-log.log, with the message saved alongside a timestamp. +A: By default, it is stored in a separate log file for Client, `/log/error-report-endpoint-log.log`, with the message saved alongside a timestamp. + +## Resources + +- [Dotkernel API on GitHub](https://github.com/dotkernel/api) diff --git a/public/md-articles/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md b/public/md-articles/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md index 3e6ec9dd..096a05cd 100644 --- a/public/md-articles/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md +++ b/public/md-articles/dotkernel-api/content-negotiation-in-dotkernel-rest-api.md @@ -11,103 +11,72 @@ language: "en" # Content Negotiation in Dotkernel REST API ## 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 is an important aspect of RESTful APIs to make it possible for diverse systems to work seamlessly together. It's based on enabling clients and servers to agree on the format and language of data they exchange. - -## What is the purpose of Content Negotiation? - -RESTful resources can support multiple representations. Each team of developers implements one of more ways (e.g. data formats) to receive requests and return responses on the server side. Efficient communication between client and server can only be guaranteed if the both sides agree on how the exchange takes place. This is especially valid if the request and response support multiple formats. The act of agreeing on a way to represent the exchanged data format is content negotiation. - -Content negotiation ensures the following: - -- **Support for diverse clients** is useful when the requested representation differs. - - e.g. `Accept: application/json` or `Accept: application/xml`. -- **Data format flexibility** can come into play when a client prefers a smaller response. - - e.g. Instead of `Accept: application/json`, use `Accept: application/msgpack` which is a binary serialization, making the response smaller and easier to transfer. -- **Language localization** will respond with content translated into the client's preferred language - - e.g. `Accept-Language: en-US`. - -## Who decides the data format? +## What is the Purpose of Content Negotiation? -There are two sides to the exchange: +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: -- The client -- The server +- **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. +- **Language localization**, e.g. `Accept-Language: en-US`, to respond with content translated into the client's preferred language. -Technically, either side can decide on how the data is transferred between the two. +## Who Decides the Data Format? -For **server-side negotiation** the server must decide based on various factors what the most appropriate format should be. This incurs assumptions that can be erroneous and the server-side implementation can also be more complex. This forces the client to adhere to the rules set up on the server-side. +Either the client or the server can decide: -For **client-side negotiation** the client lets the server know what format it prefers. This approach is more versatile and thus 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 data format: - -- **HTTP request headers** -- or **resource URI patterns**. +There are two ways to communicate the preferred data format: HTTP request headers, or resource URI patterns. ### HTTP Request Headers -The HTTP request headers `Content-Type` and `Accept` are used to determine the data format that will be sent in the request and the response. There are several types to choose from. Here are some examples: - -- text/plain -- text/html -- application/json -- application/zip -- image/gif -- image/jpeg - -Below is an example of what the keys look like in the content package. Note the `Accept` type +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 Accept: application/json ``` -If the `Accept` header is not present, the server gets to decide the format of the response. - -### Content Negotiation using URL Patterns +If the `Accept` header is not present, the server decides the response format. -Below are a couple of ways to communicate a preferred data format. +### Content Negotiation Using URL Patterns -Via the extension on the URL: +A preferred format can also be communicated via the URL extension: -``` +```shell https://www.example-api.com/record/47.xml https://www.example-api.com/record/47.json ``` -or via an extra parameter: +or via an extra query parameter: -``` +```shell https://www.example-api.com/record/47?format=xml https://www.example-api.com/record/47?format=json ``` -## Defining preferences via a quality factor - -The `Accept` header may hold multiple values with an added value that defines preference or priority. +## Defining Preferences via a Quality Factor -In this example, the client declares it accpets both json and xml formats, with json being preferred over xml, as defined by the numeric value in `q` which can be between 0 and 1. If the server can only satisfy the xml format, it will respond with that. The final alternative is if the server can't respond with either json, or xml, so it responds with what it can. +The `Accept` header can hold multiple values with an added quality value (`q`, between 0 and 1) that defines preference or priority: -``` +```shell Accept: application/json,application/xml;q=0.9,*/*;q=0.8 ``` -## How does Dotkernel API handle Content Negotiation? - -Out of the box, **Dotkernel API** uses **HTTP request headers** `Content-Type` and `Accept` to handle **client-side** content negotiation. It has both `application/json`, `application/hal+json` included. Of course, you can change these as development progresses for your project. There is also support for per-route content negotiation, if you should need it. +In this example, the client accepts both JSON and XML, with JSON preferred. If the server can only satisfy XML, it responds with that; if it can satisfy neither, it responds with whatever it can. -The configuration is done is its own configuration file. The validation is automatic and several explicit errors are handled, based on what format is supported. +## How Does Dotkernel API Handle Content Negotiation? -Check out the relevant links below for exact details on the Dotkernel implementation of content negotiation. - -## Relevant Links - -[Content Negotiation in Dotkernel API](https://docs.dotkernel.org/api-documentation/v5/core-features/content-validation/) - -[Content types on iana.org](https://www.iana.org/assignments/media-types/media-types.xhtml) +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 @@ -115,16 +84,26 @@ Check out the relevant links below for exact details on the Dotkernel implementa A: It's the act of a client and server agreeing on the format and language of the data they exchange, which is important for RESTful APIs since resources can support multiple representations. **Q: What does content negotiation ensure?** -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. +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 + +- [Content Negotiation in Dotkernel API](https://docs.dotkernel.org/api-documentation/v5/core-features/content-validation/) +- [Content types on iana.org](https://www.iana.org/assignments/media-types/media-types.xhtml) diff --git a/public/md-articles/dotkernel-api/dotkernel-api-1-0-0-released.md b/public/md-articles/dotkernel-api/dotkernel-api-1-0-0-released.md index 01beaf3b..d660315c 100644 --- a/public/md-articles/dotkernel-api/dotkernel-api-1-0-0-released.md +++ b/public/md-articles/dotkernel-api/dotkernel-api-1-0-0-released.md @@ -10,37 +10,31 @@ language: "en" # Dotkernel API 1.0.0 Released -> Dotkernel API has come a long way since this post was created. [Check out the newest version of Dotkernel API](https://www.dotkernel.com/headless-platform/version-7-adds-postgresql-native-uuid-and-php-8-5/) to stay up to date with the latest functional and security features. +> Note: Dotkernel API has come a long way since this post was created; a newer version is documented separately. -## [Dotkernel API 1.0.0](https://github.com/dotkernel/api/releases/tag/v1.0.0) was just released! +Dotkernel API 1.0.0 was just released. -  +## What is Dotkernel API? -### What is Dotkernel API? +It is a Zend Expressive 3 application aiming to help developers quickly and efficiently develop an API. -It is a [Zend Expressive 3](https://github.com/zendframework/zend-expressive) application aiming to help developers quickly and efficiently develop an API. +## How Does It Work? -  +Under the hood, it uses the following libraries: -### How does it work? +- `ezimuel/zend-expressive-api` - skeleton application on which this API is based +- `dotkernel/dot-annotated-services` (^1.1) - for handling dependency injection in your services +- `dotkernel/dot-console` (^0.1.1) - for developing console applications +- `dotkernel/dot-errorhandler` (^1.0) - which provides customizable error logging +- `dotkernel/dot-mail` (^1.0) - for sending emails via SMTP +- `zendframework/zend-expressive-authentication-oauth2` (^1.0) - for OAuth2 authentication +- `zendframework/zend-expressive-authorization-rbac` (^1.0) - for role-based permissions +- `zendframework/zend-expressive-twigrenderer` (^2.4) - for composing email bodies +- `dasprid/container-interop-doctrine` (^1.1) - database abstraction layer +- `tuupola/cors-middleware` (^0.9.4) - for automatically sending CORS headers with each request +- `swagger-api/swagger-ui` (^3.22) - for creating OpenAPI 3 documentation -Under the hood it uses the following libraries: - -- [ezimuel/zend-expressive-api](https://github.com/ezimuel/zend-expressive-api): - skeleton application on which this API is based on -- [dotkernel/dot-annotated-services](https://github.com/dotkernel/dot-annotated-services): ^1.1 - for handling dependency injection in your services -- [dotkernel/dot-console](https://github.com/dotkernel/dot-console): ^0.1.1 - for developing console applications -- [dotkernel/dot-errorhandler](https://github.com/dotkernel/dot-errorhandler): ^1.0 - which provides customizable error logging -- [dotkernel/dot-mail](https://github.com/dotkernel/dot-mail): ^1.0 - for sending emails via SMTP -- [zendframework/zend-expressive-authentication-oauth2](https://github.com/zendframework/zend-expressive-authentication-oauth2): ^1.0 - for OAuth2 authentication -- [zendframework/zend-expressive-authorization-rbac](https://github.com/zendframework/zend-expressive-authorization-rbac): ^1.0 - for role-based permissions -- [zendframework/zend-expressive-twigrenderer](https://github.com/zendframework/zend-expressive-twigrenderer): ^2.4 - for composing email bodies -- [dasprid/container-interop-doctrine](https://github.com/DASPRiD/container-interop-doctrine): ^1.1 - database abstraction layer -- [tuupola/cors-middleware](https://github.com/tuupola/cors-middleware): ^0.9.4 - for automatically sending CORS headers with each request -- [swagger-api/swagger-ui](https://github.com/swagger-api/swagger-ui): ^3.22 - for creating OpenAPI 3 documentation - -  - -### What does it offer? +## What Does It Offer? Out-of-the-box, Dotkernel API provides the following features: @@ -50,15 +44,29 @@ Out-of-the-box, Dotkernel API provides the following features: - Members are allowed to manage only their own accounts - OpenAPI 3 documentation - also an interactive interface that developers can use to integrate your API -  - ## FAQ **Q: What is Dotkernel API?** A: It is a Zend Expressive 3 application aiming to help developers quickly and efficiently develop an API. **Q: What key libraries does Dotkernel API 1.0.0 use?** -A: Among others, it's built on the ezimuel/zend-expressive-api skeleton, and uses dotkernel/dot-annotated-services for dependency injection, dotkernel/dot-console for console applications, dotkernel/dot-errorhandler for error logging, dotkernel/dot-mail for SMTP email, zend-expressive-authentication-oauth2 for OAuth2 authentication, zend-expressive-authorization-rbac for role-based permissions, and swagger-api/swagger-ui for OpenAPI 3 documentation. +A: Among others, it's built on the `ezimuel/zend-expressive-api` skeleton, and uses `dotkernel/dot-annotated-services` for dependency injection, `dotkernel/dot-console` for console applications, `dotkernel/dot-errorhandler` for error logging, `dotkernel/dot-mail` for SMTP email, `zend-expressive-authentication-oauth2` for OAuth2 authentication, `zend-expressive-authorization-rbac` for role-based permissions, and `swagger-api/swagger-ui` for OpenAPI 3 documentation. **Q: What features does Dotkernel API 1.0.0 offer out of the box?** A: Secure authentication via OAuth2, two user roles (admin and member), where admins can manage any user account and members can manage only their own, plus OpenAPI 3 documentation with an interactive interface developers can use to integrate the API. + +## Resources + +- [Dotkernel API 1.0.0 release on GitHub](https://github.com/dotkernel/api/releases/tag/v1.0.0) +- [ezimuel/zend-expressive-api](https://github.com/ezimuel/zend-expressive-api) +- [dotkernel/dot-annotated-services](https://github.com/dotkernel/dot-annotated-services) +- [dotkernel/dot-console](https://github.com/dotkernel/dot-console) +- [dotkernel/dot-errorhandler](https://github.com/dotkernel/dot-errorhandler) +- [dotkernel/dot-mail](https://github.com/dotkernel/dot-mail) +- [zendframework/zend-expressive-authentication-oauth2](https://github.com/zendframework/zend-expressive-authentication-oauth2) +- [zendframework/zend-expressive-authorization-rbac](https://github.com/zendframework/zend-expressive-authorization-rbac) +- [zendframework/zend-expressive-twigrenderer](https://github.com/zendframework/zend-expressive-twigrenderer) +- [dasprid/container-interop-doctrine](https://github.com/DASPRiD/container-interop-doctrine) +- [tuupola/cors-middleware](https://github.com/tuupola/cors-middleware) +- [swagger-api/swagger-ui](https://github.com/swagger-api/swagger-ui) +- [Newest version of Dotkernel API](https://www.dotkernel.com/headless-platform/version-7-adds-postgresql-native-uuid-and-php-8-5/) diff --git a/public/md-articles/dotkernel-api/dotkernel-api-client-side-authorization.md b/public/md-articles/dotkernel-api/dotkernel-api-client-side-authorization.md index 84ce5982..c041578c 100644 --- a/public/md-articles/dotkernel-api/dotkernel-api-client-side-authorization.md +++ b/public/md-articles/dotkernel-api/dotkernel-api-client-side-authorization.md @@ -10,13 +10,13 @@ language: "en" # Dotkernel API Client Side Authorization -**This article covers the basic authorization of a Client application which use a backend built using** [**Dotkernel API**](https://github.com/dotkernel/api) +This article covers the basic authorization of a Client application which uses a backend built using Dotkernel API. ## Authorization Request -Client application users send a **POST** request to the backend containing the following JSON object: +Client application users send a POST request to the backend containing the following JSON object: -``` +```shell { "grant_type": "password", "client_id": "{API_CLIENT}", @@ -29,9 +29,9 @@ Client application users send a **POST** request to the backend containing the f ## Authorization Response -If the credentials are correct, the **API** will return a **JSON** object containing the authentication data: +If the credentials are correct, the API will return a JSON object containing the authentication data: -``` +```shell { "token_type": "Bearer", "expires_in": 86400, @@ -40,15 +40,19 @@ If the credentials are correct, the **API** will return a **JSON** object contai } ``` -When sending **API** requests to an endpoint which requires authorization, an *Authorization* header must be present containing the following data: `"Bearer {access_token}"`, where {access_token} represents the content of the key with the same name found in the above response. +When sending API requests to an endpoint which requires authorization, an `Authorization` header must be present containing `"Bearer {access_token}"`, where `{access_token}` represents the content of the key with the same name found in the authorization response. ## FAQ **Q: What does a client send to request authorization?** -A: The client application sends a POST request to the backend with a JSON object containing grant_type (set to "password"), client_id, client_secret, scope, username/email, and password. +A: The client application sends a POST request to the backend with a JSON object containing `grant_type` (set to "password"), `client_id`, `client_secret`, `scope`, `username`/email, and `password`. **Q: What does the API return when authorization succeeds?** -A: If the credentials are correct, the API returns a JSON object containing token_type ("Bearer"), expires_in (86400 seconds), an access_token, and a refresh_token. +A: If the credentials are correct, the API returns a JSON object containing `token_type` ("Bearer"), `expires_in` (86400 seconds), an `access_token`, and a `refresh_token`. **Q: How do I use the access token in subsequent requests?** -A: When sending API requests to an endpoint that requires authorization, include an Authorization header containing "Bearer {access_token}", where {access_token} is the value returned in the authorization response. +A: When sending API requests to an endpoint that requires authorization, include an Authorization header containing `"Bearer {access_token}"`, where `{access_token}` is the value returned in the authorization response. + +## Resources + +- [Dotkernel API on GitHub](https://github.com/dotkernel/api) diff --git a/public/md-articles/dotkernel-api/dotkernel-api-server-side-authorization.md b/public/md-articles/dotkernel-api/dotkernel-api-server-side-authorization.md index 79b9015b..5a0fbb31 100644 --- a/public/md-articles/dotkernel-api/dotkernel-api-server-side-authorization.md +++ b/public/md-articles/dotkernel-api/dotkernel-api-server-side-authorization.md @@ -11,27 +11,26 @@ 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 +This article covers the basic authorization of a Server Side application built using [Dotkernel API](https://github.com/dotkernel/api). -- **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 +## Protecting an Endpoint -Configuring access to the endpoints is done by editing the following config file: +- 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 -`config/autoload/authorization.local.php` +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 it's dist file: `config/autoload/authorization.local.php.dist` and copy-paste it as the above-mentioned `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` +You should look for the array inside this config key: `zend-expressive-authorization-rbac`. -``` +```php 'zend-expressive-authorization-rbac' => , 'member' => , 'guest' => , @@ -41,37 +40,45 @@ You should look for the array inside this config key: `zend-expressive-authoriza ] ``` -Under the key **roles** you can define role inheritance. In the above example +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' => +- 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.*** +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**. +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: +### 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: +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: +### 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: +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: +### 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: +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'); ``` @@ -81,13 +88,17 @@ $app->get('/users', , 'users'); 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. +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. +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. +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. +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/md-articles/dotkernel-api/dotkernel-api-versus-laminas-api-tools.md b/public/md-articles/dotkernel-api/dotkernel-api-versus-laminas-api-tools.md index ea15aa0e..fec262c0 100644 --- a/public/md-articles/dotkernel-api/dotkernel-api-versus-laminas-api-tools.md +++ b/public/md-articles/dotkernel-api/dotkernel-api-versus-laminas-api-tools.md @@ -11,12 +11,14 @@ 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 we have created 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. +Below we have created 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) +> The table below refers to [Dotkernel API V7](https://github.com/dotkernel/api/tree/7.0). | | **API Tools (formerly Apigility)** | **Dotkernel API** | | --- | --- | --- | @@ -40,4 +42,24 @@ Below we have created an analysis of the basic features available in **Laminas A ## Note -> - Versioning is replaced by [Deprecations](https://docs.dotkernel.org/api-documentation/v6/tutorials/api-evolution/), using evolution strategy +- 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/md-articles/dotkernel-api/error-reporting-endpoint-in-dotkernel-api.md b/public/md-articles/dotkernel-api/error-reporting-endpoint-in-dotkernel-api.md index ead39c7d..c8094dca 100644 --- a/public/md-articles/dotkernel-api/error-reporting-endpoint-in-dotkernel-api.md +++ b/public/md-articles/dotkernel-api/error-reporting-endpoint-in-dotkernel-api.md @@ -11,78 +11,89 @@ 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. +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. +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 +## 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. +- 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 +## 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. +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 +### 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. +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. +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. +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 +### 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. +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`): +- 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()`. +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**: +- 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:** +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 +### 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: +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. +- The key is an alias relevant to the assigned application that uses it. +- The value is the token itself. Example: -``` +```php // ... return , ], @@ -93,13 +104,15 @@ 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`. +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) +## 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. +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({ @@ -115,12 +128,33 @@ postError(body: object): Promise { 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. +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/md-articles/dotkernel-api/how-to-implement-mailchimp-in-dotkernel-api.md b/public/md-articles/dotkernel-api/how-to-implement-mailchimp-in-dotkernel-api.md index ac9ffe50..983e168a 100644 --- a/public/md-articles/dotkernel-api/how-to-implement-mailchimp-in-dotkernel-api.md +++ b/public/md-articles/dotkernel-api/how-to-implement-mailchimp-in-dotkernel-api.md @@ -11,20 +11,21 @@ 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) - -  +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: `composer require 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: +Step 2: Create configuration file `config/autoload/mailchimp.global.php` and paste the following content inside of it: -``` +```php MailChimpFactory::class, ``` -Make sure you you add the corresponding **use**s: +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 **@Inject**ing **MailChimp::class** where it's needed. +After this, you can start using the library by @Injecting `MailChimp::class` where it's needed. ## FAQ diff --git a/public/md-articles/dotkernel-api/openapi-implementation-in-dotkernel-api.md b/public/md-articles/dotkernel-api/openapi-implementation-in-dotkernel-api.md index 3ee396fb..f9de90dd 100644 --- a/public/md-articles/dotkernel-api/openapi-implementation-in-dotkernel-api.md +++ b/public/md-articles/dotkernel-api/openapi-implementation-in-dotkernel-api.md @@ -11,82 +11,113 @@ 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? +## 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. +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. +- 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. +- 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 +## 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. +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. +- 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. +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. +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 +### Describing OpenAPI Components -All OpenAPI components require a handful of components that are universally valid for a given project. These are below: +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. +- 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: +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** +- 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/). +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 +### 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/). +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 +### 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. +- 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 +### 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/). +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 @@ -94,7 +125,8 @@ Most endpoints for your API should be protected, so to access them you are requi 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. +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. diff --git a/public/md-articles/dotkernel/adding-a-cors-implementation-to-zend-expressive.md b/public/md-articles/dotkernel/adding-a-cors-implementation-to-zend-expressive.md index a3d28b76..7a55e4f2 100644 --- a/public/md-articles/dotkernel/adding-a-cors-implementation-to-zend-expressive.md +++ b/public/md-articles/dotkernel/adding-a-cors-implementation-to-zend-expressive.md @@ -14,17 +14,15 @@ language: "en" 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. -This article is a guide on how to add a CORS implementation on an existing Dotkernel3 project. - ## The issue -If you're facing this message: - -"Access to XMLHttpRequest at 'url' has been blocked by cors policy. No 'Access-Control-Allow-Origin header is present on the requested resource." +If you're facing the error: -It means the server didn't sent the header that lets you access its data through a local client (eg.: browser). +> "Access to XMLHttpRequest at 'url' has been blocked by cors policy. +> No 'Access-Control-Allow-Origin header is present on the requested resource." -This issue is most common when trying to get some 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 error looks similar to the image below: @@ -32,27 +30,22 @@ The error looks similar to the image below: ## The solution -A simple implementation would be using [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/) - -### Adding the package to your project +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/).) -Run the following command in your project: +### 1. Add the package to your project -``` +```shell composer require tuupola/cors-middleware ``` -At the time writing this article the current package version is: 0.9.4. - -Follow the next steps to get your Zend Expressive or Dotkernel3 project **CORS friendly**. +At the time of writing, the current package version is 0.9.4. -### Create the CORS config file +### 2. Create the CORS config file -Create a **cors.global.php** file in the config/autoload directory. +Create a `cors.global.php` file in the `config/autoload` directory: -``` +```php return [ 'cors' => [ "origin" => [], @@ -66,15 +59,11 @@ return [ ]; ``` -We'll come back at this file to register the CORS middleware. - -### Creating a factory for the middleware +### 3. Create a factory for the middleware -The factory should look like the one below. +The factory extracts the config from the `cors` key (or initializes an empty array) and instantiates the Tuupola CORS middleware: -The code below extracts de config from the **cors** key if provided or initializes an empty array and instantiates the **Tuupola CORS middleware**. - -``` +```php "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. +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. + +## Resources + +- [Tuupola's Cors Middleware package](https://packagist.org/packages/tuupola/cors-middleware) +- [Implementing Tuupola CORS in Expressive (inspiration article)](https://akrabat.com/implementing-tuupola-cors-in-expressive/) diff --git a/public/md-articles/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md b/public/md-articles/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md index 52ed89ef..2c148046 100644 --- a/public/md-articles/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md +++ b/public/md-articles/dotkernel/adding-a-second-caching-layer-to-wurfl-in-dotkernel-using-apc.md @@ -13,23 +13,29 @@ language: "en" ## TL;DR On a high-traffic project using WURFL, profiling showed WURFL's default filesystem cache was costing up to a few hundred milliseconds per request. Adding a small, custom second cache layer on top of WURFL, built on APC, cut response time by an order of magnitude, down to 20-30ms. -On one of our recent projects 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. We realized that its default filesystem cache was too slow for our needs, especially with a relatively high traffic application. +## The problem -If you're not familiar with how WURFL's caching works, here's a brief introduction: +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 1. The device data is stored at first in a large, zipped XML file, with one entry for each device. 2. When first called, WURFL unzips the file and reads each device entry. -3. It then serializes the data and writes it to the cache, using and MD5 signature for the file name (or key name if the cache is not on the filesystem) +3. It then serializes the data and writes it to the cache, using an MD5 signature for the file name (or key name if the cache is not on the filesystem). 4. When a user agent is looked up, its MD5 signature is computed and then searched in the cache. -5. For a number of reasons, the data in the data file is stored as a tree, each device being a node and inheriting all the properties of the nodes above it. So, **for each look-up, a number of files have to be read and their capabilities merged to get all the capabilities of the requested device.** +5. Because the data is stored as a tree, with each device inheriting the properties of the nodes above it, **each look-up requires a number of files to be read and their capabilities merged** to get all the capabilities of the requested device. + +WURFL also has cache providers for APC and memcache, which were tried, but the results weren't impressive. -WURFL has cache providers for APC and memcache as well, which we've tried, but the results weren't impressive. +## The solution -We realized that we had the wrong approach, especially for use. The WURFL entry for a device has lots of fields that we didn't use. +The team realized their approach was wrong for their use case - the WURFL entry for a device has lots of fields that weren't actually used. -Our solution involved adding a **second cache layer**, on top of WURFL's, that only cached the fields we were interested in.  This second cache layer make use of **APC**, storing arrays of data in  **User Cache Entries**. +The solution was adding a **second cache layer** on top of WURFL's own cache, which only cached the fields that were actually needed. This second layer used **APC**, storing arrays of data in **User Cache Entries**. -This small change (under 10 lines of code), decreased our response time by an **order of magnitude**, to 20-30ms. +This small change (under 10 lines of code) decreased response time by an **order of magnitude**, down to 20-30ms. ## FAQ @@ -37,13 +43,16 @@ This small change (under 10 lines of code), decreased our response time by an ** 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/md-articles/dotkernel/adding-composer-support-in-your-dotkernel-project.md b/public/md-articles/dotkernel/adding-composer-support-in-your-dotkernel-project.md index b4682b73..d2c9cbfd 100644 --- a/public/md-articles/dotkernel/adding-composer-support-in-your-dotkernel-project.md +++ b/public/md-articles/dotkernel/adding-composer-support-in-your-dotkernel-project.md @@ -14,113 +14,98 @@ language: "en" 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. Composer auto-loads the dependencies on demand and can also auto-load custom classes . - -This article will cover the needed steps to add composer support to your Dotkernel project or even "composify" it. - -Assuming that you know how to use composer (if not you should consider reading [this article](https://www.codementor.io/php/tutorial/composer-install-php-dependency-manager)) we wil move on to your Dotkernel project "composification". - -  - ## First things first -The Dotkernel project must have a **composer.json** file so that composer can work. - -Our **composer.json** file should look like this: +The Dotkernel project must have a **composer.json** file so that Composer can work. +It should look like this: -``` +```json { -  "require" : { -    "zendframework/zendframework1" : "1.12.*", -    "mobiledetect/mobiledetectlib" : "2.8.*" -  }, -  "require-dev" : { -    "php" : ">=5.4.0" -  }, + "require" : { + "zendframework/zendframework1" : "1.12.*", + "mobiledetect/mobiledetectlib" : "2.8.*" + }, + "require-dev" : { + "php" : ">=5.4.0" + } } ``` -Note that **zend/zendframework1** may not be necessarily if you already have ZendServer running or the Zend Framework folder within **/usr/share/.** +Note: `zendframework/zendframework1` may not be necessary if you already have ZendServer running or the Zend Framework folder within `/usr/share/`. -The file above makes sure: +This file makes sure: - Zend Framework is present and at version > 1.12.* - MobileDetect is present and at version > 2.8 -- The PHP executable is at least at version > 5.4.0 (**only for development, because it is not present in the main require**) +- The PHP executable is at least at version > 5.4.0 (only for development, because it is not present in the main `require`) -  +The dependencies provided in the `require` section are also loaded for development purposes if not provided in `require-dev`. -The dependencies provided in the **require** section are also loaded for development purpose if not provided in **require-dev**. +In order to have these components installed, run the following command in your Dotkernel root path: -  - -In order to have these components instaled you must run the following command in your Dotkernel root path - -``` +```shell composer update ``` -If the *vendor* folder is present composer will check for updates, and update the packages as needed. - -If the *vendor* folder does not exist composer will create a vendor folder. This folder will contain all the requested packages. - -Composer will create an autoload file, which will be use to load our dependencies/packages. - -  +If the `vendor` folder is present, Composer will check for updates and update the packages as needed. If the `vendor` folder does not exist, Composer will create it, containing all the requested packages, along with an autoload file used to load the dependencies/packages. ## Adding Composer Support to Dotkernel -The autoload file created by composer will be used to load our packages. +The autoload file created by Composer is used to load the packages: -``` +```php $composerAutoLoaderPath = realpath(APPLICATION_PATH.'/vendor/autoload.php'); require_once($composerAutoLoaderPath); ``` -So far so good, but what if the file does not exist or composer is not present? - -First we must make sure the composer autoload path exists and only load the dependencies if composer 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'); $composerEnabled = file_exists($composerAutoLoaderPath); -if($composerEnabled == true) -{ -    require_once($composerAutoLoaderPath); -} -else -{ +if ($composerEnabled == true) { + require_once($composerAutoLoaderPath); +} else { // handle the error gracefully - // or load fallbacks - if exist + // or load fallbacks - if exist } ``` -The variable **$composerEnabled** will be true only if the composer path exists so the application behavior can be controlled if composer is not present. +The variable `$composerEnabled` will be true only if the Composer path exists, so the application behavior can be controlled if Composer is not present. Later on, the packages can be used like this: -``` +```php use VendorName\PackageName\ClassName as MyDependency; $myDependency = new MyDependency($neededArguments); $myDependency->doSomething(); ``` +This article works for any **Dotkernel 1.x** version if your server is running **PHP >5.4.0**. + ## 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. **Q: Which Dotkernel versions does this apply to?** A: The article states it works for any Dotkernel 1.x version, as long as the server is running PHP greater than 5.4.0. + +## Resources + +- [Composer install / PHP dependency manager tutorial](https://www.codementor.io/php/tutorial/composer-install-php-dependency-manager) diff --git a/public/md-articles/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md b/public/md-articles/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md index 54905d21..70cc5c14 100644 --- a/public/md-articles/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md +++ b/public/md-articles/dotkernel/adding-windows-10-os-and-browser-detection-in-dotkernel-projects.md @@ -14,24 +14,21 @@ language: "en" 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. -Recently we have added the Windows 8, 8.1 and 10 OS icon and Microsoft's Edge browser icon. +## Upgrade steps -In this article we will have the icon upgrade guide. +![Icons Patch](/uploads/article/019f8a80-cc47-710e-9ef1-b2257262e376/icons.png) +*The new Icons listed in User and Admin Logins* -![Icons Patch](/uploads/article/019f8a80-cc47-710e-9ef1-b2257262e376/icons.png)The new Icons listed in User and Admin Logins +1. Make sure your project is running version **1.5.0** or **newer**. +2. Download the [patch](http://www.dotkernel.com/download/?did=46). +3. Extract the archive into a folder, e.g. `icons_patch`. +4. Create a backup of your project before continuing (recommended). +5. Copy all the files in the `icons_patch` folder into your Dotkernel project. +6. You will be prompted to replace 2 files - replace them and agree to merge the folders' content (other files will be added, not replaced). +7. Clear the cache for changes to take effect, since the OS and browser XMLs are cached (see "Dotkernel Reserved Variable Names for Caching", the "Browser & OS" section). +8. You can now delete the `icons_patch` folder, or keep it to patch another project. -  - -1. Make sure your project is running on version **1.5.0** or **newer** -2. Download the [patch](http://www.dotkernel.com/download/?did=46) -3. Extract the archive in a folder, let's name it **icons_patch** -4. We recommend creating a backup of your project before you continue -5. Now copy all the files in the **icons_patch** folder in your Dotkernel -6. You will be prompted to replace 2 files, simply replace the files and agree to merge the folders content (files will be added, not replaced this time) -7. You need to clear the cache for changes to take effect, the os and browser xml's are cached. For more information read [this article](http://www.dotkernel.com/dotkernel/dotkernel-reserved-variable-names-for-caching/) (look for **Browser & OS**) -8. You can now delete the **icons_patch** folder or use it to patch another project - -List of affected files: +## Affected files ``` M /configs/useragent/browser.xml @@ -40,9 +37,7 @@ A /images/browsers/edge.png A /images/os/windows_metro.png ``` -**M** stands for **modify** - -**A** stands for **add** +`M` stands for **modify**, `A` stands for **add**. ## FAQ @@ -57,3 +52,8 @@ A: Because the OS and browser XML files are cached, so the new icons won't show **Q: Will applying the patch overwrite existing files?** A: You'll be prompted to replace 2 files (browser.xml and os.xml) and should agree, and also agree to merge the folders' contents since the other files listed are added rather than replaced. + +## Resources + +- [Icon patch download](http://www.dotkernel.com/download/?did=46) +- [Dotkernel Reserved Variable Names for Caching](http://www.dotkernel.com/dotkernel/dotkernel-reserved-variable-names-for-caching) diff --git a/public/md-articles/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md b/public/md-articles/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md index 85366a17..800652cd 100644 --- a/public/md-articles/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md +++ b/public/md-articles/dotkernel/autologin-using-cookie-remember-me-in-dotkernel.md @@ -14,17 +14,11 @@ language: "en" 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. -## Autologin using Cookie / Remember Me in Dotkernel - -This feature is used to automatically log the user who chooses this by checking the remember me box. - -Implemented in [Dotkernel Frontend](https://github.com/dotkernel/frontend) starting from Release 3.3.0. - ## Add remember me button to user interface -To add remember me button to user interface, navigate to **src/User/templates/user/login.html.twig** and under password element add the following code: +Navigate to `src/User/templates/user/login.html.twig` and, under the password element, add: -``` +```twig
{% set rememberMe = form.get('rememberMe') %} {{ formElement(rememberMe) }} @@ -32,9 +26,9 @@ To add remember me button to user interface, navigate to **src/User/templates/us
``` -After you've added the button to your template, navigate to **src/User/src/Form/LoginForm.php** and add the following element to your form: +Then navigate to `src/User/src/Form/LoginForm.php` and add the following element to your form: -``` +```php $this->add([ 'name' => 'rememberMe', 'type' => 'checkbox', @@ -46,9 +40,9 @@ $this->add([ ]); ``` -After you've added the new element navigate **src/User/src/InputFilter/LoginInputFilter.php** and add the following code, this will add a filter to the remember me element added previously. +Then navigate to `src/User/src/InputFilter/LoginInputFilter.php` and add a filter for the new element: -``` +```php $this->add([ 'name' => 'rememberMe', 'filters' => [ @@ -63,9 +57,9 @@ $this->add([ ]); ``` -To add style to your remember me button navigate to **src/App/assets/scss/components/_profile.scss** and add the following css: +To style the button, navigate to `src/App/assets/scss/components/_profile.scss` and add: -``` +```scss .remember-me-checkbox { input { display: block; @@ -77,40 +71,29 @@ To add style to your remember me button navigate to **src/App/assets/scss/compon } ``` -After you have made all the changes it's time to compile the css in order to implement the button in interface, to do that run the following command: +After making the changes, compile the CSS so the button styling takes effect: -``` +```shell npm run prod ``` -## **Add functionality to remember me button** - -Now that you've added the button let's move on to its functionality, first you have to navigate to src/User/src/Entity, create a new entity named UserRememberMe.php and modifiy it as in [UserRememberMe](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Entity/UserRememberMe.php) - -After you've created the entity, you have to create migration file for the new table using the following command: - -``` -vendor/bin/phinx create --configuration=config/migrations.php RememberUserSchema -``` - -< - -class="mb-3">After the migration file is created modify it as in [user_remember_schema](https://www.dotkernel.com/dotkernel/autologin-using-cookie-remember-me-in-dotkernel/) and run the following command to add it to you database: - -``` -vendor/bin/phinx migrate --configuration=config/migrations.php -``` - -The table generated by migration is used to store data from coockie. The stored data will help to login the user utomatically. - -The next step is to create a new middleware, navigate to **src/App/src/Middleware** and create a new file named **RememberMeMiddleware.php** and modify it as in [RememberMeMiddleware](https://github.com/dotkernel/frontend/blob/3.0/src/App/src/Middleware/RememberMeMiddleware.php). - -After you've created the new middleware navigate to **config/pipeline.php** and add it as in [pipeline](https://github.com/dotkernel/frontend/blob/3.0/config/pipeline.php). - -In order to generate the cookie in the next steps you have to add a new key to your local.php, to do that navigate to **config/autoload/local.php** and add the following code: - -``` -'rememberMe' => [ +## Add functionality to remember me button + +1. Navigate to `src/User/src/Entity`, create a new entity named `UserRememberMe.php`, modeled on [UserRememberMe](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Entity/UserRememberMe.php). +2. Create a migration file for the new table: + ```shell + vendor/bin/phinx create --configuration=config/migrations.php RememberUserSchema + ``` +3. Modify the generated migration file as in [user_remember_schema](https://www.dotkernel.com/dotkernel/autologin-cookie-remember-me-feature/), then run it against the database: + ```shell + vendor/bin/phinx migrate --configuration=config/migrations.php + ``` + The table generated by the migration is used to store data from the cookie, which helps log the user in automatically. +4. Create a new middleware at `src/App/src/Middleware/RememberMeMiddleware.php`, modeled on [RememberMeMiddleware](https://github.com/dotkernel/frontend/blob/3.0/src/App/src/Middleware/RememberMeMiddleware.php). +5. Register the middleware in `config/pipeline.php` as shown in [pipeline](https://github.com/dotkernel/frontend/blob/3.0/config/pipeline.php). +6. To generate the cookie, add a new key to `config/autoload/local.php`: + ```php + 'rememberMe' => [ 'cookie' => [ 'name' => 'rememberMe', 'lifetime' => 3600 * 24 * 30, @@ -119,15 +102,10 @@ In order to generate the cookie in the next steps you have to add a new key to y 'httponly' => true ] ], -``` - -In the following step you have to edit your **src/User/src/Service/UserService.php**. First you have to add 2 new properties, **$defaultSessionManager** used to get config and **$repository** used to get repository. After you have added those properties add the following methods: **getRepository(), addRememberMeToken(), deleteRememberMeCookie()**. as in [UserService](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Service/UserService.php). - -(don't forget to add all new methods to interface if needed) - -Now, let's move to **src/User/src/Repository/UserRepository.php** and add the following methods: **saveRememberUser(), getRememberUser(), findRememberMeUser(), deleteExpiredCookies(), removeRememberUser()** as in [UserRepository](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Repository/UserRepository.php). - -The last step is to go to **src/User/src/Controller/UserController.php**, add a new property called **$config** and edit your **loginAction()** and **logoutAction()** as in [UserController](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Controller/UserController.php). + ``` +7. Edit `src/User/src/Service/UserService.php`: add two new properties, `$defaultSessionManager` (to get config) and `$repository` (to get repository), then add the methods `getRepository()`, `addRememberMeToken()`, `deleteRememberMeCookie()` as in [UserService](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Service/UserService.php) (and add the new methods to the interface if needed). +8. Edit `src/User/src/Repository/UserRepository.php` and add the methods `saveRememberUser()`, `getRememberUser()`, `findRememberMeUser()`, `deleteExpiredCookies()`, `removeRememberUser()` as in [UserRepository](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Repository/UserRepository.php). +9. Edit `src/User/src/Controller/UserController.php`: add a new property called `$config`, and edit `loginAction()` and `logoutAction()` as in [UserController](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Controller/UserController.php). ## FAQ @@ -148,3 +126,14 @@ A: It's used to store data from the cookie, which helps log the user in automati **Q: How do you apply the new remember-me button styles?** A: Add the CSS to src/App/assets/scss/components/_profile.scss, then run npm run prod to compile the CSS so the button styling takes effect. + +## Resources + +- [Dotkernel Frontend on GitHub](https://github.com/dotkernel/frontend) +- [UserRememberMe entity example](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Entity/UserRememberMe.php) +- [Remember user schema migration example](https://www.dotkernel.com/dotkernel/autologin-cookie-remember-me-feature/) +- [RememberMeMiddleware example](https://github.com/dotkernel/frontend/blob/3.0/src/App/src/Middleware/RememberMeMiddleware.php) +- [pipeline.php example](https://github.com/dotkernel/frontend/blob/3.0/config/pipeline.php) +- [UserService example](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Service/UserService.php) +- [UserRepository example](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Repository/UserRepository.php) +- [UserController example](https://github.com/dotkernel/frontend/blob/3.0/src/User/src/Controller/UserController.php) diff --git a/public/md-articles/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md b/public/md-articles/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md index afb7281f..f93fa85d 100644 --- a/public/md-articles/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md +++ b/public/md-articles/dotkernel/avoid-routing-through-bootstrap-of-non-existent-files.md @@ -10,25 +10,24 @@ 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 user is logged off, because the session ID is different now. +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. -To avoid this, **below** the following line +To avoid this, below the following line: ``` RewriteEngine On ``` -add the line : +add the line: ``` -RewriteCond %{REQUEST_FILENAME} (\.gif|\.jpg|\.png|\.css|\.js)$ [OR] +RewriteCond %{REQUEST_FILENAME} (\.gif|\.jpg|\.png|\.css|\.js)$ ``` -Save and don't forget to test. - -Even though it's a well known fact that real men do not test... +Save, and don't forget to test. ## FAQ diff --git a/public/md-articles/dotkernel/caching-in-dotkernel-using-zend-framework.md b/public/md-articles/dotkernel/caching-in-dotkernel-using-zend-framework.md index 0f2c488e..ab301054 100644 --- a/public/md-articles/dotkernel/caching-in-dotkernel-using-zend-framework.md +++ b/public/md-articles/dotkernel/caching-in-dotkernel-using-zend-framework.md @@ -14,34 +14,26 @@ language: "en" 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. -It's very expensive to load configurations and settings from XML files, on every requests. - -First because of latency of accessing files from hard drive, second because of the XML file parsing burden. - -Because of that , we implemented in upcoming 1.8 version of Dotkernel a cache layer where to store **router, acl_role, menu, options(including seo_xml), browser_xml, os_xml, test** between requests. More information about the variables which Dotkernel cache by default follow this link: [Dotkernel Reserved Variable Names for Caching](http://www.dotkernel.com/dotkernel/dotkernel-reserved-variable-names-for-caching) - -We are implementing 2 different cache factories to choose from: **apc** (or **apcu** for newest PHP installations) and **file**. - ## 1. Configuring the cache -The configuration can be set from /configs/application.ini, you can choose if you use the caching system, how long your cache stays valid, the cache namespace, and the storage provider (**File** or **APC**). I would disable the cache in development mode if I were you. - -For more info about the configuration and help configuring the cache see: [Configuring the Cache in Dotkernel](http://www.dotkernel.com/dotkernel/configuring-the-cache-in-dotkernel/). - -## 2. Using 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 cache is automatically loaded in the initialization and stored in the Registry. +## 2. Using the cache -Loading the caching engine is not needed because it is already loaded on kernel initialization (*see **Dot_Kernel**::**initialize**($startTime)*)*,* but if you would like to use caching for other purposes (where you are not initializing the kernel), the loading syntax is the following: +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(); ``` -Below is a simple object caching sample, yes, you can also cache objects. - Note: The cache key must match the following RegEx pattern: **[A-Za-z0-9_]*** +Note: the cache key must match a specific RegEx pattern. -``` +Example of object caching: + +```php $id = 'MyCachedKey'; $obj = new stdClass(); $obj->text = 'I am a cached text'; @@ -53,13 +45,10 @@ Dot_Cache::save(obj, $id); $value = Dot_Cache::load($id); // checking if we have the object in cache -if($value !== false) -{ +if ($value !== false) { // assuming we only need the text value from the object echo $value->text; -} -else -{ +} else { echo 'no value cached for '. $id ; } ``` @@ -73,10 +62,17 @@ 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(). + +## Resources + +- [Dotkernel Reserved Variable Names for Caching](http://www.dotkernel.com/dotkernel/dotkernel-reserved-variable-names-for-caching) +- [Configuring the Cache in Dotkernel](http://www.dotkernel.com/dotkernel/configuring-the-cache-in-dotkernel/) diff --git a/public/md-articles/dotkernel/camelcase-table-names-in-mysql-on-windows.md b/public/md-articles/dotkernel/camelcase-table-names-in-mysql-on-windows.md index 1313b261..5c545ac5 100644 --- a/public/md-articles/dotkernel/camelcase-table-names-in-mysql-on-windows.md +++ b/public/md-articles/dotkernel/camelcase-table-names-in-mysql-on-windows.md @@ -10,15 +10,15 @@ language: "en" # camelCase Table Names in MySQL on Windows -If you are using a WAMP stack, like WAMP or XAMPP, and try to create a table in camelCase ( example: **adminLogin**) you will notice that camelCase is not working, table name will be lowercase: **adminlogin**. In order to fix this, you need to add to your my.cnf file the line: +If you are using a WAMP stack, like WAMP or XAMPP, and try to create a table in camelCase (example: `adminLogin`), you will notice that camelCase is not working - the table name will be lowercase: `adminlogin`. + +In order to fix this, add the following line to your `my.cnf` file: ``` lower_case_table_names=2 ``` -and restart mysql. - -More on that here: [http://dev.mysql.com/doc/refman/4.1/en/server-system-variables.html#sysvar_lower_case_table_names](http://dev.mysql.com/doc/refman/4.1/en/server-system-variables.html#sysvar_lower_case_table_names) +and restart MySQL. ## FAQ @@ -26,4 +26,8 @@ More on that here: [http://dev.mysql.com/doc/refman/4.1/en/server-system-variabl A: A table created with a camelCase name, for example adminLogin, ends up stored as all lowercase, e.g. adminlogin, instead. **Q: How do you fix it?** -A: Add the line lower_case_table_names=2 to your my.cnf file and restart MySQL. +A: Add the line `lower_case_table_names=2` to your my.cnf file and restart MySQL. + +## Resources + +- [MySQL lower_case_table_names documentation](http://dev.mysql.com/doc/refman/4.1/en/server-system-variables.html#sysvar_lower_case_table_names) diff --git a/public/md-articles/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md b/public/md-articles/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md index b05b7966..2b43175d 100644 --- a/public/md-articles/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md +++ b/public/md-articles/dotkernel/commitment-to-php-new-zend-certified-engineers-zce-in-our-team.md @@ -11,14 +11,16 @@ 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. -Now we are 5 :-) - -That mean we are really taking PHP  into serious , and at least we have good technical skills. - -[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=) +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=). ## FAQ **Q: How many Zend Certified Engineers does the team have?** A: According to the article, 2 more team members passed the ZCE exam, bringing the team's total to 5 Zend Certified Engineers. + +## Resources + +- [Zend Yellow Pages listing](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/md-articles/dotkernel/configuring-the-cache-in-dotkernel.md b/public/md-articles/dotkernel/configuring-the-cache-in-dotkernel.md index aa515e28..ccbd0279 100644 --- a/public/md-articles/dotkernel/configuring-the-cache-in-dotkernel.md +++ b/public/md-articles/dotkernel/configuring-the-cache-in-dotkernel.md @@ -11,53 +11,46 @@ 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: +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) +## Main Cache Settings (Cache Frontend) -The main cache settings within the **application.ini** file should look like this: +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 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 variables cached will be usable before they will need to be re-cached. +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) -## 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. -The individual cache settings are optional but we highly recommend that you have theese 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 +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 article beginning. - +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" diff --git a/public/md-articles/dotkernel/dependency-injection-made-easy-in-laminas-mezzio-applications.md b/public/md-articles/dotkernel/dependency-injection-made-easy-in-laminas-mezzio-applications.md index 6d80f383..7b9fa486 100644 --- a/public/md-articles/dotkernel/dependency-injection-made-easy-in-laminas-mezzio-applications.md +++ b/public/md-articles/dotkernel/dependency-injection-made-easy-in-laminas-mezzio-applications.md @@ -11,23 +11,27 @@ 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. +> 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. -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. +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. +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. +> 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( @@ -38,9 +42,10 @@ class UserHandler implements RequestHandlerInterface } ``` -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: +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 { /** @@ -61,7 +66,7 @@ class UserHandlerFactory And in the config provider, we would have the following: -``` +```php public function getDependencies(): array { return @@ -69,9 +74,9 @@ public function getDependencies(): array } ``` -In one more example, let's look at the real-world required dependencies for `UserService`, the dependency that is required for `UserHandle`. +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( @@ -90,21 +95,20 @@ class UserService implements UserServiceInterface } ``` -Now consider that we need to build the factory for this and update it when we add a new dependency, and so on. Also to build the logic in the factory to handle any dependencies missing from the container. Painful right? +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. +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 `#[Inject(...)]` attribute to the constructor definition to specify which dependencies should be injected. +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 { - #[Inject( - UserServiceInterface::class, - "config", - )] + # public function __construct( protected UserServiceInterface $userService, protected array $config, @@ -113,9 +117,9 @@ class UserHandler implements RequestHandlerInterface } ``` -Add the `Dot\DependencyInjection\Factory\AttributedServiceFactory` class to your `ConfigProvider` +Add the `Dot\DependencyInjection\Factory\AttributedServiceFactory` class to your `ConfigProvider`: -``` +```php public function getDependencies(): array { return @@ -123,11 +127,12 @@ public function getDependencies(): array } ``` -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. +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; @@ -150,9 +155,10 @@ class UserService implements UserServiceInterface } ``` -## And, that's not all. +## And, That's Not All. -If you use doctrine and 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/). +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 diff --git a/public/md-articles/dotkernel/detecting-mobile-devices-in-dotkernel-1-6-0.md b/public/md-articles/dotkernel/detecting-mobile-devices-in-dotkernel-1-6-0.md index e9914ae1..a5b236aa 100644 --- a/public/md-articles/dotkernel/detecting-mobile-devices-in-dotkernel-1-6-0.md +++ b/public/md-articles/dotkernel/detecting-mobile-devices-in-dotkernel-1-6-0.md @@ -11,27 +11,24 @@ 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 comming with some changes how we are detecting mobile devices, this changes are because of the new Wurfl Cloud integration. - -This version of Dotkernel is not comming anymore with a working built in method for mobile detection, so first we have to configure it. - -- go to scientiamobile website and register for an Wurfl Cloud account -- choose **device_os** and **mobile_browser** to your account and save -- go to API Keys and copy the right key in application.ini - -  +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. -We choosed device_os and mobile_browser capabilities because with these two capabilities we can get some extra capabilities (isMobile, isSmartPhone, isIphone, isAndroid, isBlackberry, is Symbian and is WindowsMobile) using our built in methods. +- 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. -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 Dot_UserAgent_WurflCloud class. +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 +Wurfl Cloud setting in application.ini: -``` +```ini resources.useragent.wurflcloud.active = TRUE resources.useragent.wurflcloud.redirect = TRUE resources.useragent.wurflcloud.cache = TRUE @@ -41,23 +38,29 @@ 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 is TRUE your visitators from frontend will be redirected to mobile module (default: TRUE) **cache** - cache 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** - API Key from WURFL Cloud account (change this with your key) **lib_dir** - the wurfl cloud library location in Dotkernel (don't change this, just if you want to move the library) +- 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 one Dot_UserAgent_WurflCloud wich is using the Wurfl Cloud API adapter. +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: +## Example of Dot_UserAgent Usage in Dotkernel -Get Wurfl configuration +Get Wurfl configuration: -``` +```php $wurflConf = $registry->configuration->resources->useragent->wurflcloud; ``` -Note: You can have more Wurfl configurations if you have more libraries like Wurfl Package (GPL) +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 +If Wurfl is active, then get device info: -``` +```php if($wurflConf->active) { $deviceInfo = Dot_UserAgent :: getDeviceInfo($_SERVER); @@ -65,9 +68,9 @@ if($wurflConf->active) } ``` -If detected device is an mobile device we will save device info in database and redirect it to the mobile controller +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) { diff --git a/public/md-articles/dotkernel/disable-wurfl-redirect-for-mobile-browsers.md b/public/md-articles/dotkernel/disable-wurfl-redirect-for-mobile-browsers.md index 7ef958a2..8dab81ae 100644 --- a/public/md-articles/dotkernel/disable-wurfl-redirect-for-mobile-browsers.md +++ b/public/md-articles/dotkernel/disable-wurfl-redirect-for-mobile-browsers.md @@ -11,23 +11,24 @@ 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). -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): +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: +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)) ``` diff --git a/public/md-articles/dotkernel/disambiguation-dotkernel-1-and-dotkernel-3.md b/public/md-articles/dotkernel/disambiguation-dotkernel-1-and-dotkernel-3.md index 6cd90015..77ec8032 100644 --- a/public/md-articles/dotkernel/disambiguation-dotkernel-1-and-dotkernel-3.md +++ b/public/md-articles/dotkernel/disambiguation-dotkernel-1-and-dotkernel-3.md @@ -11,17 +11,18 @@ 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 the meaning behind 'Dotkernel'? +## What Is the Meaning Behind 'Dotkernel'? The name **Dotkernel** symbiotically combines the string **Dot,** as a representation of the Internet, and **Kernel**, the quintessential components of any IT application. In other words, **Dotkernel** aims to become the starting point for development Internet applications and hence ensure increased development productivity and run-time performance. -## What was Dotkernel 1? +## What Was Dotkernel 1? Dotkernel 1 was a ***PHP* *Application Framework***, built on top of Zend Framework 1 (ZF1). @@ -29,7 +30,7 @@ It had the first public release in July 2010. It was tightly coupled with **Zend The latest version is **1.8 Long Term Support**. It will not be getting any new releases or bugfixes because Zend Framework 1 is also not supported. If you are still using either Dotkernel 1 or Zend Framework 1, you need to refactor your code to the [Dotkernel Headless Platform](https://docs.dotkernel.org/headless-documentation/). -## What is Dotkernel? +## What Is Dotkernel? A **collection** of PSR-15 Middleware applications built on top of the [**Mezzio**](https://docs.mezzio.dev/mezzio/v3/getting-started/quick-start/) microframework. It is composed of a set of custom and extended [**Laminas**](https://docs.laminas.dev/) components. @@ -38,8 +39,8 @@ Dotkernel architecture is based on **Middleware**. Dotkernel implements the foll Currently, there are three applications: - API - - Admin - - Queue +- Admin +- Queue ## Dotkernel = Dotkernel 1 or the new Dotkernel? @@ -48,7 +49,7 @@ In posts older than 2017 **Dotkernel 1** was referred to as **Dotkernel** becaus ## 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. +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. diff --git a/public/md-articles/dotkernel/doctrine-cache-using-symfony-cache.md b/public/md-articles/dotkernel/doctrine-cache-using-symfony-cache.md index c6c70efc..0e653cf3 100644 --- a/public/md-articles/dotkernel/doctrine-cache-using-symfony-cache.md +++ b/public/md-articles/dotkernel/doctrine-cache-using-symfony-cache.md @@ -11,15 +11,21 @@ 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. -When it comes to web development, performance is one of the critical elements that influence the success of an application. Developers focus on improving response times and overall speed to enhance the user experience. +When it comes to web development, performance is one of the critical elements that influence the success of an application. +Developers focus on improving response times and overall speed to enhance the user experience. -When a user visits a website or interacts with a web application, various resources such as images, scripts, and database queries are requested from the server. Retrieving these resources can sometimes be time-consuming, especially if they require complex processing or querying a database. +When a user visits a website or interacts with a web application, various resources such as images, scripts, and database queries are requested from the server. +Retrieving these resources can sometimes be time-consuming, especially if they require complex processing or querying a database. -To speed up this process and improve overall performance, developers implement caching mechanisms. When data is first requested, it's stored in a cache. Then, when subsequent requests for the same data are made, the application can retrieve it from the cache instead of fetching it from the original source. This reduces the time it takes to serve the content to the user because accessing data from the cache is typically much faster than retrieving it from the original source. +To speed up this process and improve overall performance, developers implement caching mechanisms. +When data is first requested, it's stored in a cache. +Then, when subsequent requests for the same data are made, the application can retrieve it from the cache instead of fetching it from the original source. +This reduces the time it takes to serve the content to the user because accessing data from the cache is typically much faster than retrieving it from the original source. > This article is a follow-up to the [previous article](https://www.dotkernel.com/how-to/doctrine-cache-in-mezzio-and-dotkernel/) where we tackled the caching topic. @@ -31,30 +37,32 @@ In this article our focus will be on enabling the [dot-cache](https://packagist. 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 we continue with the configuration process we need 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: +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: -1. **array** - stores data in-memory -2. **filesystem** - stores data on local disk files +- 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 the 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. +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. +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).** +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 `configautoloaddoctrine.global.php`, in the `doctrine.configuration.orm_default` key add the following entry: +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', @@ -63,64 +71,74 @@ In `configautoloaddoctrine.global.php`, in the `doctrine.configuration.orm_defa ], ``` -Next, under the `doctrine` key add the following items: +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: ** +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** +### 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 -### **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. -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. -### **Query cache** +### Hydration Cache -In a production environment, it's strongly recommended to cache the resulted 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. +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. -### **Hydration cache** +## How to Use -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, need to add the  `#` attribute like in the following example: +To enable caching for entities, you need to add the `#` attribute like in the following example: -``` -###class Admin extends AbstractEntity implements AdminInterface{} +```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/latest/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. - -``` -$this->getQueryBuilder() ->select('admin') ->from(Admin::class, 'admin') ->setCacheable(true) ->getQuery() ->getResult(); +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. +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 diff --git a/public/md-articles/dotkernel/doctrine-enum-implementation-in-dotkernel.md b/public/md-articles/dotkernel/doctrine-enum-implementation-in-dotkernel.md index c794116f..84a6f134 100644 --- a/public/md-articles/dotkernel/doctrine-enum-implementation-in-dotkernel.md +++ b/public/md-articles/dotkernel/doctrine-enum-implementation-in-dotkernel.md @@ -11,21 +11,19 @@ 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. -The update of `doctrine/orm` to version 3.2.0 saw the introduction of **EnumType** columns. The enum type was introduced in **PHP 8.1**. This new data type is now implemented in Dotkernel, on both the PHP side and the database side. - -Below we will discuss some technical aspects behind this update. You can review the full update in [this Dotkernel API Pull Request](https://github.com/dotkernel/api/pull/339/files). +## Doctrine's Approach -## 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. -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 -### Old setup - -``` +```php #[Entity] class Card { @@ -43,9 +41,9 @@ class Card } ``` -### New setup +### New Setup -``` +```php #[Entity] class Card { @@ -59,21 +57,27 @@ class Card } ``` -> Note that the type `Types::ENUM` part is still required if we want to have an actual `enum` column in MariaDB. We still default to `Types::STRING` or `TYPES::INTEGER` for columns types with a PHP enum as this is the more portable solution and the safer default. +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 +## Dotkernel's Approach -### Old setup +### 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 agrred upon set of values, but the database is independent from it. If you edit a value manually in the database, any string is accepted. +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 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. +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 = [ @@ -84,14 +88,14 @@ public const STATUSES = [ The column for the ORM was defined like this, as a simple string, with `pending` as its default value: -``` +```php #[ORM\Column(name: "status", type: "string", length: 20)] protected string $status = self::STATUS_PENDING; ``` Obviously, the `getStatus` and `setStatus` also work with strings: -``` +```php public function getStatus(): string { return $this->status; @@ -103,19 +107,20 @@ public function setStatus(string $status): self } ``` -### New setup +### 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. +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. +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. +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 @@ -127,9 +132,9 @@ enum UserStatusEnum: string 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. +`AbstractEnumType` must be extended by any future custom enum type. -``` +```php namespace Api\User\DBAL\Types; use Api\App\DBAL\Types\AbstractEnumType; @@ -151,11 +156,11 @@ class UserStatusEnumType extends AbstractEnumType } ``` -> If you create your own enum types, make sure to update the `NAME` constant and the value returned by `getEnumClass`. +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, @@ -165,7 +170,7 @@ Let's register the custom type in `config/autoload/doctrine.global.php` under th The filtering is updated in `src/User/src/InputFilter/Input/StatusInput.php`: -``` +```php $this->getFilterChain() ->attachByName(StringTrim::class) ->attachByName(StripTags::class) @@ -182,14 +187,14 @@ The above ensures that the new `UserStatusEnum` class is used for the `status` c The `User` entity uses the new `UserStatusEnum` class. -``` +```php #[ORM\Column(type: 'user_status_enum', options: ['default' => UserStatusEnum::Pending])] protected UserStatusEnum $status = UserStatusEnum::Pending; ``` The `status` getter and setter are also updated: -``` +```php public function getStatus(): UserStatusEnum { return $this->status; @@ -201,9 +206,10 @@ public function setStatus(UserStatusEnum $status): self } ``` -Dotkernel checks the user status during login in `src/User/src/Repository/UserRepository.php`. If the user is not activated, the login is rejected. +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['status'] !== UserStatusEnum::Active) { throw new OAuthServerException(Message::USER_NOT_ACTIVATED, 6, 'inactive_user', 401); } @@ -211,7 +217,7 @@ if ($clientEntity->getName() === 'frontend' && $result['status'] !== UserStatusE A new user is created using the `enum` type and `pending` as the default. -``` +```php $user = (new User()) ->setDetail($detail) ->setIdentity($data['identity']) @@ -245,19 +251,14 @@ 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: +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. -## Relevant links - -- [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) - ## FAQ **Q: What update triggered this change to Dotkernel's enum handling?** @@ -273,7 +274,13 @@ A: It made it easy to add more values to the value set, though the article notes 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 MariaDB. +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/md-articles/dotkernel/dotboost-technologies-products-and-services-north-american-relaunch.md b/public/md-articles/dotkernel/dotboost-technologies-products-and-services-north-american-relaunch.md index 1e0ad2fc..5db3060a 100644 --- a/public/md-articles/dotkernel/dotboost-technologies-products-and-services-north-american-relaunch.md +++ b/public/md-articles/dotkernel/dotboost-technologies-products-and-services-north-american-relaunch.md @@ -11,19 +11,28 @@ 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. -**A new style and advanced approach to accompany the Dotkernel source release** +## 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. -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. -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. +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. +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 diff --git a/public/md-articles/dotkernel/dotkernel-1-2-0-release.md b/public/md-articles/dotkernel/dotkernel-1-2-0-release.md index e135e983..85d3f2e9 100644 --- a/public/md-articles/dotkernel/dotkernel-1-2-0-release.md +++ b/public/md-articles/dotkernel/dotkernel-1-2-0-release.md @@ -11,27 +11,39 @@ 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. -Finally we reached Dotkernel 1.2.0 milestone. +## 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 -Since the previous released 1.1.2, some changes have been made. +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. -- On database, we changed the names and structure of tables to respect database naming convention. [*http://www.dotkernel.com/dotkernel/dotkernel-database-naming-conventions-for-mysql/*](../dotkernel/dotkernel-database-naming-conventions-for-mysql/) +Another change made in the configs folder is resource.xml, which contains the configuration values for the controllers of each module. -- 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 *frontend* module. Note that one dots can be part of multiple modules. (For example, *“user”* dots belong to *frontend* and *admin* module). For each dots, the configurations values have been added to xml files which are stored in *configs/dots* folder. In the preview versions, this values where hard-coded in the php files. +To be easier to start an application from Dotkernel, in the admin module, there are now the following dots: admin, user and system. -- Another change made in *configs* folder is *resource.xml*, which contains the configuration values for the controllers of each module. +## Library Class Updates -To be easier to start an application from Dotkernel, in admin module, there are now the following dots:  admin, user and system. +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). -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 his 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/](../php-development/protection-against-sql-injection-using-pdo-and-zend-framework/)* +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/) +For more details, see [ChangeLog 1.2.0](http://www.dotkernel.com/changelog/1-2-0/). ## FAQ diff --git a/public/md-articles/dotkernel/dotkernel-1-2-2-release.md b/public/md-articles/dotkernel/dotkernel-1-2-2-release.md index 53290e96..95c3fa76 100644 --- a/public/md-articles/dotkernel/dotkernel-1-2-2-release.md +++ b/public/md-articles/dotkernel/dotkernel-1-2-2-release.md @@ -11,30 +11,43 @@ language: "en" # Dotkernel 1.2.2 release ## 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. -Yesterday, we released **Dotkernel 1.2.2**. It contains some bug fixes: +## Bug fixes in 1.2.2 + +- **31** - captcha errors try/catch +- **32** - pagination issue +- **33** - admin wrong link +- **34** - Acunetix scan results from July 24th (notices and one fatal error) +- **35** - update copyright line in files -- [31](http://www.dotkernel.net/view.php?id=31) – captcha errors try catch -- [32](http://www.dotkernel.net/view.php?id=32) – pagination issue -- [33](http://www.dotkernel.net/view.php?id=33) – Admin wrong link -- [34](http://www.dotkernel.net/view.php?id=34) – Acunetix results July 24th ( notices and one fatal error) -- [35](http://www.dotkernel.net/view.php?id=35) – update copyright line in files +**Note:** because of bug 35, all PHP files changed in this release. -For more details see [ChangeLog 1.2.2](../changelog/1-2-2/). To get only the changed files from 1.2.1 to 1.2.2, download the [upgrade](../download/?did=17) file +## Upgrading -**Note***: because of the Bug 35, all php files have changed. To see what else has changed, check the [Dotkernel Tracker](http://www.dotkernel.net/) or the [Dotkernel WebSVN](http://websvn.dotkernel.net/listing.php?repname=Dotkernel+ver.+1) . +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. -*P.S.* On July 22, 2010 we released *Dotkernel 1.2.1.* You can check the [ChangeLog 1.2.1](../changelog/1-2-1/) or download [the upgrade 1.2.1](../download/?did=14) zip file. +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. ## FAQ **Q: What does the Dotkernel 1.2.2 release include?** -A: Dotkernel 1.2.2 is a bug-fix release that closes five issues: captcha error handling (try/catch), a pagination issue, a wrong admin link, notices and a fatal error found by an Acunetix scan, and an update to the copyright line in files. +A: It's a bug-fix release that closes five issues: captcha error handling (try/catch), a pagination issue, a wrong admin link, notices and a fatal error found by an Acunetix scan, and an update to the copyright line in files. **Q: Why did all PHP files change in the 1.2.2 release?** A: Because of the fix for bug 35, which updated the copyright line, every PHP file in the codebase was touched, which is why the note in the post warns that all PHP files have changed. **Q: How can I upgrade from a previous version to 1.2.2?** A: You can download just the changed files from 1.2.1 to 1.2.2 using the upgrade package linked in the post, or check the ChangeLog 1.2.2 for full details of what changed. + +## Resources + +- ChangeLog 1.2.2 (linked in the original post as `../changelog/1-2-2/`) +- Upgrade package for 1.2.2 (linked in the original post as `../download/?did=17`) +- Dotkernel Tracker: http://www.dotkernel.net/ +- Dotkernel WebSVN: http://websvn.dotkernel.net/listing.php?repname=Dotkernel+ver.+1 +- ChangeLog 1.2.1 (linked in the original post as `../changelog/1-2-1/`) +- Upgrade package for 1.2.1 (linked in the original post as `../download/?did=14`) diff --git a/public/md-articles/dotkernel/dotkernel-1-3-0-release.md b/public/md-articles/dotkernel/dotkernel-1-3-0-release.md index c90ce5ec..10389c78 100644 --- a/public/md-articles/dotkernel/dotkernel-1-3-0-release.md +++ b/public/md-articles/dotkernel/dotkernel-1-3-0-release.md @@ -11,28 +11,37 @@ language: "en" # Dotkernel 1.3.0 release ## 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](../download/?did=23) is released at last. It contains important changes and new features. +## 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`). -- [64](http://www.dotkernel.net/view.php?id=64): **[Feature]** Skin switcher in admin - closed. +### Protecting member-only links -The admin skin can be customized. There are several readymade skins like: *blue*, *brown*, *gray* and *green*. To set the admin skin, change the value of *settings.admin.skin* from application (e.g. *settings.admin.skin = green*). +To protect a link so only logged-in members can access it, add this line in the controller file above the code that should require login: -- [76](http://www.dotkernel.net/view.php?id=76): **[Bugs]** Want-Url in frontend - closed. +```php +Dot_Auth::checkIdentity(); +``` -To protect a link that is accessible by members only, add this line in the controller file to protect what is below it: *Dot_Auth::checkIdentity();* +### XML reorganization -- [77](http://www.dotkernel.net/view.php?id=77): **[Bugs]** Dot_Sessions / rename - closed. - [70](http://www.dotkernel.net/view.php?id=70): **[Bugs]** Menu issue in Admin and frontend - closed. - [73](http://www.dotkernel.net/view.php?id=73): **[Bugs]** Naming consistency Upper-lower case in url - closed. - [71](http://www.dotkernel.net/view.php?id=71): **[Bugs]** XSS forgot password - closed. - [72](http://www.dotkernel.net/view.php?id=72): **[Bugs]** scan result Oct 12th - closed. - [63](http://www.dotkernel.net/view.php?id=63): **[Bugs]** Scan results Oct 1, 2010 on 1.3.0 RC - closed. - [69](http://www.dotkernel.net/view.php?id=69): **[Bugs]** reorganization of XMl files - closed. +Some XML files from the configs folder were changed. `resource.xml` was deleted and its content was split between two new files, `route.xml` and `dots.xml`. -Some xml files from configs folder have been changed to encompass the current needs of Dotkernel. *resource.xml* has been deleted and its content split between *route.xml* and *dots.xml*. Check the manual to find more about [route.xml](../docs/router-xml/) and [dots.xml](../docs/dots-xml/) +### Other closed issues -- [67](http://www.dotkernel.net/view.php?id=67): **[Bugs]** Security Issue / test controller - closed. - [68](http://www.dotkernel.net/view.php?id=68): **[Bugs]** The canonical URL isn't escaped - closed. - [66](http://www.dotkernel.net/view.php?id=66): **[Bugs]** Blank line at the beginning of every file in the frontend - closed. - [60](http://www.dotkernel.net/view.php?id=60): **[Bugs]** 1.3.0 as Release Candidate Friday Oct 1st - closed. - [62](http://www.dotkernel.net/view.php?id=62): **[Bugs]** Admin text box class dojo - closed. - [55](http://www.dotkernel.net/view.php?id=55): **[Bugs]** geoIP extension: record by name + list in dashboard admin GEOIP version and build - closed. - [59](http://www.dotkernel.net/view.php?id=59): **[Bugs]** Admin listings # - closed. - [61](http://www.dotkernel.net/view.php?id=61): **[Bugs]** Admin Add Transporter not working - closed. - [58](http://www.dotkernel.net/view.php?id=58): **[Bugs]** Admin : list stuff, div float - closed. - [57](http://www.dotkernel.net/view.php?id=57): **[Bugs]** OS name on admin/on mouse over - closed. - [53](http://www.dotkernel.net/view.php?id=53): **[Bugs]** drop-down list in admin/user logins - closed. - [52](http://www.dotkernel.net/view.php?id=52): **[Bugs]** Admin hide debug bar in Login page - closed. +The release also closed a number of other tracked issues, covering: the Dot_Sessions rename, menu issues in Admin and frontend, URL casing consistency, an XSS issue in the forgot-password flow, several security scan results, admin listing/UI fixes, a GeoIP extension listing feature, and formatting cleanup (blank lines, brace placement) across the frontend files. -For more details see [ChangeLog 1.3.0](../changelog/1-3-0/). +## Compatibility note -**Note***: because of the bug [69](http://www.dotkernel.net/view.php?id=69), this release is not compatible with the previous versions. To see what else has changed, check [Dotkernel Tracker](http://www.dotkernel.net/) or [Dotkernel WebSVN](http://websvn.dotkernel.net/listing.php?repname=Dotkernel). +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 @@ -47,3 +56,12 @@ A: resource.xml was deleted and its content split between two new files, route.x **Q: Is Dotkernel 1.3.0 backward compatible with earlier versions?** A: No. Because of the XML file reorganization (bug 69), 1.3.0 is not compatible with previous versions. + +## Resources + +- Dotkernel 1.3.0 download (linked in the original post as `../download/?did=23`) +- ChangeLog 1.3.0 (linked in the original post as `../changelog/1-3-0/`) +- route.xml documentation (linked in the original post as `../docs/router-xml/`) +- dots.xml documentation (linked in the original post as `../docs/dots-xml/`) +- Dotkernel Tracker: http://www.dotkernel.net/ +- Dotkernel WebSVN: http://websvn.dotkernel.net/listing.php?repname=Dotkernel diff --git a/public/md-articles/dotkernel/dotkernel-1-3-2-release.md b/public/md-articles/dotkernel/dotkernel-1-3-2-release.md index 3383a78c..77aca70a 100644 --- a/public/md-articles/dotkernel/dotkernel-1-3-2-release.md +++ b/public/md-articles/dotkernel/dotkernel-1-3-2-release.md @@ -11,30 +11,29 @@ language: "en" # Dotkernel 1.3.2 release ## TL;DR -Released just before the winter holidays, Dotkernel 1.3.2 is mainly a maintenance release: it contains many bug fixes, some refactoring, and a few minor features. -Before the winter holiday we came with a new release: [Dotkernel 1.3.2](http://www.dotkernel.com/download/?did=27) It contains many bug fixes, some refactoring and a few minor features. +Released just before the winter holidays, Dotkernel 1.3.2 is mainly a maintenance release: it contains many bug fixes, some refactoring, and a few minor features. -Bugs fixes: +## Bug fixes -- [0000111](http://www.dotkernel.net/view.php?id=111): **[Bugs]** Css phpinfo page admin -- [0000115](http://www.dotkernel.net/view.php?id=115): **[Bugs]** warning in admin dashboard for file -- [0000108](http://www.dotkernel.net/view.php?id=108): **[Bugs]** WUFRL cache in admin -- [0000094](http://www.dotkernel.net/view.php?id=94): **[Bugs]** WURLF version -- [0000112](http://www.dotkernel.net/view.php?id=112): **[Bugs]** Dot_Paginator Bug -- [0000109](http://www.dotkernel.net/view.php?id=109): **[Bugs]** Zend Paginator double query ? -- [0000089](http://www.dotkernel.net/view.php?id=89): **[Bugs]** Database naming convention -- [0000097](http://www.dotkernel.net/view.php?id=97): **[Bugs]** Database normalisation/refactor +- CSS issue on the admin phpinfo page +- Warning in the admin dashboard for a file +- WURFL cache issue in admin +- WURFL version issue +- Dot_Paginator bug +- Zend Paginator double-query issue +- Database naming convention issue +- Database normalisation/refactor -Minor features: +## Minor features -- [0000096](http://www.dotkernel.net/view.php?id=96): **[Feature]** validIP , class Dot_Kernel refactor -- [0000099](http://www.dotkernel.net/view.php?id=99): **[Feature]** WURFL date and api version in admin +- Refactor of validIP in the Dot_Kernel class +- WURFL date and API version shown in admin -Refactoring: +## Refactoring -- [0000110](http://www.dotkernel.net/view.php?id=110): **[REFACTOR]** Zend_Paginator -- [0000107](http://www.dotkernel.net/view.php?id=107): **[REFACTOR]** Add a dojo dijit theme to Dotkernel +- Zend_Paginator refactoring +- Added a dojo dijit theme to Dotkernel ## FAQ @@ -45,4 +44,9 @@ 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 + +- Dotkernel 1.3.2 download: http://www.dotkernel.com/download/?did=27 diff --git a/public/md-articles/dotkernel/dotkernel-1-5-0-released.md b/public/md-articles/dotkernel/dotkernel-1-5-0-released.md index 0a0f4c60..db62c3f6 100644 --- a/public/md-articles/dotkernel/dotkernel-1-5-0-released.md +++ b/public/md-articles/dotkernel/dotkernel-1-5-0-released.md @@ -11,42 +11,43 @@ language: "en" # Dotkernel 1.5.0 Released ## 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, Dotkernel 1.5.0 was just released. Due to the large amount of changes and the long time spent in development, we chose to skip 1.4 and go straight to 1.5.0. +## Why skip straight to 1.5.0? -Here are a few of the many changes to Dotkernel in the latest release: +Due to the large amount of changes and the long time spent in development, the team chose to skip 1.4 and go straight to 1.5.0. ## Highlights of 1.5.0 ### Switched from Dojo to jQuery -Starting with 1.5.0 we've [switched from using Dojo to jQuery](http://www.dotkernel.com/javascript/intro-to-jquery/). This doesn't mean you can't still use Dojo in your own projects, but only jQuery will be used and maintained in the Dotkernel distribution. +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 -We've redesigned the admin site, with new themes, and a dropdown menu, as well as a new and simpler design for the front-end. +The admin site was redesigned, with new themes and a dropdown menu, along with a new and simpler design for the front-end. ### Model inheritance -Up until now, there was a lot of code duplication in models. For example, in the user model, you might have a *getUserById* function in the admin as well as the frontend. When you've got more models and more modules, your project can start having a lot of copy-pasted code. - -To prevent this, we've introduced a *Dot_Model* class, and a way to define global models that are inherited in the admin and frontend. This way, you can have *User* class in the admin that only has methods specific to the admin module, a *User* class in the frontend that only has code specific for the frontend, and they both inherit the *Dot_Model_User* class which will have all 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 -We've changed the way the controller name is parsed, so that you can have controller with multiple words, split with dashes, without breaking the coding standard (for example, *www.example.com/search-article* will call *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 -We've changed the structure of the registry, for more about this, please check [this blog post](http://www.dotkernel.com/dotkernel/zend-registry-usage-in-dotkernel/). - -  +The structure of the registry was changed; more details are covered in a separate blog post on Zend Registry usage in Dotkernel. -There have been about 250 commits in our SVN repository since the latest release, so we can't cover all changes in this blog post. Please [download Dotkernel 1.5.0](http://www.dotkernel.com/download/?did=33) try it out yourself and tell us what you think. +## Scale of the release -  +There were about 250 commits in the SVN repository since the previous release, so the blog post could not cover every change. ## FAQ @@ -54,13 +55,21 @@ There have been about 250 commits in our SVN repository since the latest release 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. **Q: How much changed in the 1.5.0 release?** A: About 250 commits went into the SVN repository since the previous release, so the blog post only covers the highlights - the full Dotkernel 1.5.0 download is available to try out. + +## Resources + +- Intro to jQuery: http://www.dotkernel.com/javascript/intro-to-jquery/ +- Zend Registry usage in Dotkernel: http://www.dotkernel.com/dotkernel/zend-registry-usage-in-dotkernel/ +- Dotkernel 1.5.0 download: http://www.dotkernel.com/download/?did=33 diff --git a/public/md-articles/dotkernel/dotkernel-1-8-0-lts-released.md b/public/md-articles/dotkernel/dotkernel-1-8-0-lts-released.md index 0d6174fa..ee76cf66 100644 --- a/public/md-articles/dotkernel/dotkernel-1-8-0-lts-released.md +++ b/public/md-articles/dotkernel/dotkernel-1-8-0-lts-released.md @@ -11,65 +11,56 @@ language: "en" # Dotkernel 1.8.0 LTS Released ## 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 just released. - -## **What is LTS?** - -Long-term support (LTS) is a type of special versions or editions of software designed to be supported for a longer than normal period. It is particularly applicable to open-source software projects. It contains many bug fixes, some refactoring and a few minor features. Find more details read [this article](http://www.dotkernel.com/long-term-support). +## What is LTS? -  - -Here are a few of the many changes to Dotkernel in the latest release: +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) -  - ### Plugin Architecture -Starting with 1.8.0 we will start using Plugins to make the Dotkernel extending easier. We'll keep you up to date about how you create and use a plugin. +Starting with 1.8.0, Dotkernel uses Plugins to make extending the framework easier. ### New design -We've redesigned the admin module and the frontend module is now mobile-friendly, but you can still use the mobile module. +The admin module was redesigned and the frontend module is now mobile-friendly, while the separate mobile module remains available. -### Loads Faster +### Loads faster -The Dotkernel framework just got a big boost because it supports APC & File Caching within the framework, all the XML's and config files are cached in order to maximize response speed for more information about caching and how to cache your data see [this article](http://www.dotkernel.com/dotkernel/caching-in-dotkernel-using-zend-framework/). +The framework now supports APC and File Caching, with all XML and config files cached in order to maximize response speed. -### Easier Request Handling +### Easier request handling -We've added a new class, Dot_Request, which lets you have control over the request data before you use it, for example the variables $_SERVER, $_GET and $_POST are only used within controllers. +A new class, `Dot_Request`, gives control over the request data before use - for example, so that `$_SERVER`, `$_GET`, and `$_POST` are only accessed from within controllers. ### Features added -- API with Rate Limit - we've added a simple API with a single key authentification and a simple implementation of a rate limit (see /configs/application.ini - section params.api) -- Cache System - Built on the Zend_Cache backends, provides caching within Dotkernel, but also in library, more details about this and how it works can be found [here](http://www.dotkernel.com/dotkernel/dotkernel-reserved-variable-names-for-caching/). +- API with Rate Limit - a simple API with single-key authentication and a basic rate limit implementation (configurable in `/configs/application.ini`, section `params.api`) +- Cache System - built on Zend_Cache backends, providing caching within Dotkernel and in library code ### Other changes -- Removed WURFL integration, Dotkernel can detect wether you use a mobile device separately now, the WURFL Library can be added as a Plugin from now on -- Removed multiple SMTP Transporter, it can be added as a plugin -- Security scan in Admin Dashboard - you can now see which are the recommended settings (especially security related) for Dotkernel to work at it's best -- Admin fail logins are no longer sent to the first admin, they are sent to all developers found in *devEmails* within *settings* table in the database -- Alert System - Alerts can be sent to all the developers to notify them if something goes wrong, for more information about alerts read [this article](http://www.dotkernel.com/dotkernel/how-to-use-alerts-in-dotkernel/). +- Removed WURFL integration - mobile device detection is now handled separately; WURFL can be added as a plugin +- Removed support for multiple SMTP transporters - it can be added as a plugin +- Security scan in the Admin Dashboard, showing recommended (especially security-related) settings +- Admin failed-login notifications are now sent to all developers listed in `devEmails` (within the `settings` table), not just the first admin +- Alert System - alerts can be sent to all developers to notify them if something goes wrong -### Bug Fixes +### Bug fixes -- [0000289](http://dotkernel.net/view.php?id=289): **[Bugs]** seo.xml will cause error on same varname for two modules instead of overwriting -- [0000249](http://dotkernel.net/view.php?id=249): **[Bugs]** email sent twice -- [0000275](http://dotkernel.net/view.php?id=275): **[Bugs]** wrong unwritable warning on nginx +- `seo.xml` caused an error when two modules used the same variable name instead of overwriting it +- Emails were sent twice +- A wrong "unwritable" warning appeared on nginx -  +## Scale of the release -  - -There have been a lot of commits in our SVN repository since the latest release, so we can't cover all changes in this blog post. Please [download Dotkernel 1.8.0 (LTS)](http://www.dotkernel.com/download/?did=41) try it out yourself and tell us what you think. - -  +There were a lot of commits in the SVN repository since the previous release, so the blog post only covers the highlights. ## FAQ @@ -90,3 +81,11 @@ A: WURFL integration was removed (mobile device detection is now handled separat **Q: What security-related additions does 1.8.0 include?** A: A security scan in the Admin Dashboard shows recommended settings, admin failed-login notifications are sent to all developers listed in devEmails (not just the first admin), and a new Alert System can notify developers if something goes wrong. + +## Resources + +- What is LTS: http://www.dotkernel.com/long-term-support +- Caching in Dotkernel using Zend Framework: http://www.dotkernel.com/dotkernel/caching-in-dotkernel-using-zend-framework/ +- Dotkernel reserved variable names for caching: http://www.dotkernel.com/dotkernel/dotkernel-reserved-variable-names-for-caching/ +- How to use alerts in Dotkernel: http://www.dotkernel.com/dotkernel/how-to-use-alerts-in-dotkernel/ +- Dotkernel 1.8.0 (LTS) download: http://www.dotkernel.com/download/?did=41 diff --git a/public/md-articles/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md b/public/md-articles/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md index 962b96d6..2b3beb03 100644 --- a/public/md-articles/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md +++ b/public/md-articles/dotkernel/dotkernel-1-8-1-upgrade-from-1-8-0-released.md @@ -11,20 +11,19 @@ language: "en" # Dotkernel 1.8.1 + Upgrade from 1.8.0 Released ## 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 just released. - -Changes to Dotkernel in the latest release: - Enhanced Cache Support, which means you can use tags in your cache system if the host suports it - -Here are some useful download links: +## What's new -[Dotkernel 1.8.1](http://www.dotkernel.com/download/?did=42) +- Enhanced Cache Support - you can use tags in your cache system if the host supports it -[Upgrade from Dotkernel 1.8.0](http://www.dotkernel.com/download/?did=43) +## Download links -[Dotkernel 1.8.0 (LTS)](http://www.dotkernel.com/download/?did=41) +- Dotkernel 1.8.1 (full package) +- Upgrade from Dotkernel 1.8.0 +- Dotkernel 1.8.0 (LTS) ## FAQ @@ -33,3 +32,9 @@ A: The main change is Enhanced Cache Support, which means you can use tags in yo **Q: How do I upgrade from 1.8.0 to 1.8.1?** A: The post provides a dedicated "Upgrade from Dotkernel 1.8.0" download link, separate from the full Dotkernel 1.8.1 package and the original Dotkernel 1.8.0 (LTS) download. + +## Resources + +- Dotkernel 1.8.1: http://www.dotkernel.com/download/?did=42 +- Upgrade from Dotkernel 1.8.0: http://www.dotkernel.com/download/?did=43 +- Dotkernel 1.8.0 (LTS): http://www.dotkernel.com/download/?did=41 diff --git a/public/md-articles/dotkernel/dotkernel-coding-standard.md b/public/md-articles/dotkernel/dotkernel-coding-standard.md index d4ce2ef3..648b295e 100644 --- a/public/md-articles/dotkernel/dotkernel-coding-standard.md +++ b/public/md-articles/dotkernel/dotkernel-coding-standard.md @@ -11,34 +11,37 @@ language: "en" # Dotkernel Coding Standard ## TL;DR + Dotkernel is a "skeleton" of Zend Framework and borrows its coding standard from the ZF Coding Standard, with a small number of exceptions covering indentation, naming conventions, and brace placement. -**Dotkernel** will be a "skeleton"of [**Zend Framework**](http://framework.zend.com/). Dotkernel borrowed the coding standard from Zend Framework: **[ZF Coding Standard](http://framework.zend.com/manual/en/coding-standard.php-file-formatting.html)** with some exceptions. +## Indentation -In what follows, we will make remarks only on those features that are slightly different in the coding standards of Dotkernel. +Indentation is made with tabs, not spaces (per section B.2.2 of the Zend Framework Coding Standard). -[**B.2. PHP File Formatting**](http://framework.zend.com/manual/en/coding-standard.php-file-formatting.html) +## Naming conventions -- [**B.2.2. Indentation**](http://framework.zend.com/manual/en/coding-standard.php-file-formatting.html#coding-standard.php-file-formatting.indentation) +Dotkernel uses camel naming conventions, with these Dotkernel-specific rules: -[**B.3. Naming Conventions**](http://framework.zend.com/manual/en/coding-standard.naming-conventions.html) +| Element | Convention | Example | +|---|---|---| +| Classes | Start with `Dot_` | `Dot_Templates` | +| Interfaces | End with the string "Interface" | `Dot_Db_Interface` | +| Filenames | Always use the `.php` extension, no fancy extensions | `.php`, not `.inc` | -Camel naming convention +## Control statements - brace placement -- [**B.3.1. Classes**](http://framework.zend.com/manual/en/coding-standard.naming-conventions.html#coding-standard.naming-conventions.classes) -- **[B.3.2. Interfaces](http://framework.zend.com/manual/en/coding-standard.naming-conventions.html#coding-standard.naming-conventions.interfaces)** -- **[B.3.3. Filenames](http://framework.zend.com/manual/en/coding-standard.naming-conventions.html#coding-standard.naming-conventions.filenames)** +Every opening curly brace `{` starts on its own new line after the statement, and its matching closing brace `}` is also placed on its own new line, aligned in the same column as the opening brace, for better indentation of the code. -[**B.4.6. Control Statements**](http://framework.zend.com/manual/en/coding-standard.coding-style.html#coding-standard.coding-style.control-statements) every starting curly brace **}** after a statement starts on a new line, end it's closing curly brace **}** will be on a new line too. The start and end braces must be on the same column (for better indentation of the code) e.g: +Example: -``` +```php if ($a != 2) { $a = 2; } ``` -``` +```php if ($a != 2) { $a = 2; @@ -62,3 +65,14 @@ A: Classes start with the prefix Dot_ (e.g. Dot_Templates), interfaces end with **Q: How should curly braces be placed for control statements?** A: Every opening curly brace starts on its own new line after the statement, and its matching closing brace also goes on a new line, aligned in the same column as the opening brace, for better indentation of the code. + +## Resources + +- Zend Framework: http://framework.zend.com/ +- ZF Coding Standard: http://framework.zend.com/manual/en/coding-standard.php-file-formatting.html +- ZF Coding Standard - Indentation: http://framework.zend.com/manual/en/coding-standard.php-file-formatting.html#coding-standard.php-file-formatting.indentation +- ZF Coding Standard - Naming Conventions: http://framework.zend.com/manual/en/coding-standard.naming-conventions.html +- ZF Coding Standard - Classes: http://framework.zend.com/manual/en/coding-standard.naming-conventions.html#coding-standard.naming-conventions.classes +- ZF Coding Standard - Interfaces: http://framework.zend.com/manual/en/coding-standard.naming-conventions.html#coding-standard.naming-conventions.interfaces +- ZF Coding Standard - Filenames: http://framework.zend.com/manual/en/coding-standard.naming-conventions.html#coding-standard.naming-conventions.filenames +- ZF Coding Standard - Control Statements: http://framework.zend.com/manual/en/coding-standard.coding-style.html#coding-standard.coding-style.control-statements diff --git a/public/md-articles/dotkernel/dotkernel-database-naming-conventions-for-mysql.md b/public/md-articles/dotkernel/dotkernel-database-naming-conventions-for-mysql.md index 91f664c2..6fc2141f 100644 --- a/public/md-articles/dotkernel/dotkernel-database-naming-conventions-for-mysql.md +++ b/public/md-articles/dotkernel/dotkernel-database-naming-conventions-for-mysql.md @@ -11,22 +11,24 @@ language: "en" # Dotkernel Database Naming Conventions for MySQL ## 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 borrows the database naming conventions from [FaZend: Rules of naming of database tables and columns](http://fazend.com/a/2009-11-DataNaming.html). FaZend is an open-source PHP framework based on Zend Framework. **Database naming conventions for tables and columns:** +## Database naming conventions for tables and columns -- Singular table names only (e.g. *user*, *category*, *product*, *order, orderProduct*) -- Every table must have an auto-incrementing integer column id -- ZF-like names of columns and tables (e.g. *user::isAdmin*, *orderProduct::product*) -- ~~Foreign keys must have the same names as reference tables~~ -- Foreign keys must have the same names as reference tables + the name of the referenced column **Example:** table referenced: *admin* , column name: *Id* so the column will be *adminId* -- Pattern for CONSTRAINT name : FK_referencedTableName_tableName **Example:** CONSTRAINT `FK_admin_adminLogin` -- SQL keywords are capitalized (e.g. SELECT, INT) +- Singular table names only (e.g. `user`, `category`, `product`, `order`, `orderProduct`) +- Every table must have an auto-incrementing integer column `id` +- ZF-like names of columns and tables (e.g. `user::isAdmin`, `orderProduct::product`) +- Foreign keys must have the same name as the referenced table plus the name of the referenced column. + Example: table referenced is `admin`, column name `Id`, so the foreign key column will be `adminId`. +- Pattern for CONSTRAINT name: `FK_referencedTableName_tableName`. + Example: `CONSTRAINT FK_admin_adminLogin`. +- SQL keywords are capitalized (e.g. `SELECT`, `INT`) -**Example of proper SQL file formatting and naming:** +## Example of proper SQL file formatting and naming -``` +```sql CREATE TABLE IF NOT EXISTS `user` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, @@ -35,7 +37,7 @@ CREATE TABLE IF NOT EXISTS `user` `email` VARCHAR(100) NOT NULL, `firstName` VARCHAR(255) NOT NULL, `lastName` VARCHAR(255) NOT NULL, - `dateCreated` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `dateCreated` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `userType` INT(11) NOT NULL AUTO_INCREMENT `isActive` ENUM('0','1') NOT NULL DEFAULT '1', PRIMARY KEY (`id`), @@ -50,7 +52,9 @@ CREATE TABLE IF NOT EXISTS `user` AUTO_INCREMENT=1 ; ``` -**Conclusion:** The names of database tables and columns must follow *camelLetter* as naming conventions.s +## Conclusion + +The names of database tables and columns must follow camelLetter naming conventions. ## FAQ @@ -61,10 +65,15 @@ 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`. **Q: What casing convention applies to table/column names and to SQL keywords?** A: Table and column names must follow camelLetter naming conventions, while SQL keywords such as SELECT and INT are capitalized. + +## Resources + +- FaZend: Rules of naming of database tables and columns: http://fazend.com/a/2009-11-DataNaming.html diff --git a/public/md-articles/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md b/public/md-articles/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md index 649c3cf4..076cea00 100644 --- a/public/md-articles/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md +++ b/public/md-articles/dotkernel/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.md @@ -11,49 +11,47 @@ language: "en" # Dotkernel Light - Starting with Mezzio microframework and Laminas components ## 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 the smallest complete Mezzio application that includes only the bare-bones essentials. Though simpler than Frontend, it's perfect for: - -- A presentation site, -- An introduction into the Mezzio microframework architecture, -- A starting point for a more complex project where you have full control over functionality. - ## Goal -**Dotkernel Light** is the smallest complete Mezzio application, designed at the same time to be a fast-start example of using **Mezzio microframework**, as well as of using an **entry-level version** of Dotkernel Frontend. Primarily, its purpose is to present the **novice developer** with as few moving parts as possible. It's also a perfect starting point for the more **advanced developer** who wants 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-box components **encourages the active exploration of functionality** required by your application. Basically, 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 -We designed Dotkernel Light to be the smallest complete Mezzio application, a stripped-down version of Dotkernel Frontend. Just like Dotkernel Frontend, Dotkernel Light is built on top of the Mezzio microframework using Laminas components. The big difference is the limited features and thus the lower number of packages. This 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. -Currently, Light contains this **functionality**: +### Functionality retained - Routing - Templating - Error handling - Tests and code quality checks -Compared to Frontend, Light had these **items removed**: +### Items removed compared to Frontend -- Doctrine and all database related stuff +- Doctrine and all database-related stuff - Sessions/Cookies/Flash messages - Authentication/Authorization - Dependency Injection -- Mail related stuff +- Mail-related stuff - Navigation - CORS - Forms/Validators/InputFilters - User module - Contact module - Plugin module -- CSS/JS code that was no longer in use due to the above module removals +- CSS/JS code no longer in use due to the above module removals - Instructions from README.md that are no longer needed -As expected, several packages used by Frontend are not required: +### Packages no longer required - dotkernel/dot-authorization - dotkernel/dot-data-fixtures @@ -73,21 +71,14 @@ As expected, several packages used by Frontend are not required: - mezzio/mezzio-tooling - rector/rector -## Useful links - -- [Dotkernel Light GitHub repository](https://github.com/dotkernel/light) -- [Working demo of Dotkernel Light](https://light.dotkernel.net/) -- [Dotkernel Light documentation](https://docs.dotkernel.org/light-documentation/) -- [Laminas Project](https://getlaminas.org/) -- [Documentation of Mezzio](https://docs.mezzio.dev/) - ## FAQ **Q: What is Dotkernel Light?** -A: Dotkernel Light is the smallest complete Mezzio application, a PSR-15 pipeline, routing and templating, with nothing to strip out. It's a good base for 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 the smallest complete Mezzio application. It's a fast-start example of using the Mezzio microframework as well as an entry-level version of Dotkernel Frontend. It presents the novice developer with as few moving parts as possible, while still allowing the more advanced developer to have full control of the platform's functionality. +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. **Q: What functionality does Dotkernel Light retain?** A: It keeps routing, templating, error handling, and tests and code quality checks. @@ -97,3 +88,11 @@ A: Items removed include Doctrine and all database related stuff, sessions/cooki **Q: Which packages are no longer required in Dotkernel Light?** A: Packages such as dotkernel/dot-authorization, dotkernel/dot-mail, dotkernel/dot-session, dotkernel/dot-navigation, dotkernel/dot-flashmessenger, laminas/laminas-form, mezzio/mezzio-cors, and several others used by Frontend are not required. + +## Resources + +- Dotkernel Light GitHub repository: https://github.com/dotkernel/light +- Working demo of Dotkernel Light: https://light.dotkernel.net/ +- Dotkernel Light documentation: https://docs.dotkernel.org/light-documentation/ +- Laminas Project: https://getlaminas.org/ +- Documentation of Mezzio: https://docs.mezzio.dev/ diff --git a/public/md-articles/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md b/public/md-articles/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md index 1317b343..ebd417e2 100644 --- a/public/md-articles/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md +++ b/public/md-articles/dotkernel/dotkernel-light-the-best-choice-for-your-presentation-site.md @@ -11,54 +11,45 @@ language: "en" # Dotkernel Light: the best choice for your presentation site ## 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 the smallest complete Mezzio application and a good starting point for a project if you want to have **full control over the functionality** it contains. It **can be expanded** into something more complex with the integration of packages based on your requirements. - -Its out-of-box functionality is suitable for a **presentation site**: - -- Routing -- Templating -- Error handling -- Tests and code quality checks - -Presentation sites don't require features that are present in Dotkernel Frontend. The goal of Dotkernel Light is to have **no clutter**, so these features are removed: - -- Everything related to the database -- Sessions/Cookies/Flash messages -- Authentication/Authorization -- Dependency Injection -- Mail related stuff -- Navigation -- CORS -- Forms/Validators/InputFilters -- User module -- Contact module -- Plugin module - -## The goal of this article - -In this article we explore how to use Dotkernel Light for a simple presentation site. We will mention what files to focus on to teach you how to add more pages of content to your site and how to manage their assets. - -### Adding new pages - -The first step is to add the new pages in `src/Page/src/Controller/PageController.php`. This means adding an `Action` function for each page, as seen below. - -``` - public function examplePageAction(): ResponseInterface - { - return new HtmlResponse( - $this->template->render('page::example-template') - ); - } +## What's included vs. removed + +| Included out of the box | Removed (not needed for a presentation site) | +|---|---| +| Routing | Everything related to the database | +| Templating | Sessions/Cookies/Flash messages | +| Error handling | Authentication/Authorization | +| Tests and code quality checks | Dependency Injection | +| | Mail related stuff | +| | Navigation | +| | CORS | +| | Forms/Validators/InputFilters | +| | User module | +| | Contact module | +| | Plugin module | + +## Adding new pages + +1. Add an `Action` function for the page in `src/Page/src/Controller/PageController.php`, for example: + +```php +public function examplePageAction(): ResponseInterface +{ + return new HtmlResponse( + $this->template->render('page::example-template') + ); +} ``` -> The url for the new page in this example is `/page/example-page`. + The URL for this example page would be `/page/example-page`. -Each page has its own template, so the next step is to create the template files in the `src/Page/templates/page/` folder. For the example above, the `src/Page/templates/page/example-template.html.twig` file was created. We won't include the entire code here, just the basic building blocks. The `content` block is where your page copy goes. +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' %} {% block title %}Page Title{% endblock %} @@ -78,33 +69,26 @@ Each page has its own template, so the next step is to create the template files {% endblock %} ``` -> Make sure to check the header for any fonts your content requires. - -If you haven't already done so, make sure the `npm` is installed and running during your updates with `npm run watch` or run this command after the edits are completed `npm run prod`. + Make sure to check the header for any fonts your content requires. -The assets should be copied under the `src/App/assets/` folder. -These are the default asset folders: +3. Place assets under `src/App/assets/`, in the default folders: + - `src/App/assets/fonts` + - `src/App/assets/images` + - `src/App/assets/js` + - `src/App/assets/scss` -- src/App/assets/fonts -- src/App/assets/images -- src/App/assets/js -- src/App/assets/scss + Make sure `npm` is installed and running during updates with `npm run watch`, or run `npm run prod` after edits are completed. ## Optional items ### Twitter and OpenGraph cards -If you want to promote the pages on other platforms, a helpful item is the header section in the `src/App/templates/layout/default.html.twig` file. This is where the Twitter (X) and OpenGraph cards should be placed. +To promote pages on other platforms, edit the header section in `src/App/templates/layout/default.html.twig`, where the Twitter (X) and OpenGraph cards are placed. Update all items based on your page content. -Make sure to update all items based on your page content. +- `{{ url('home') }}` generates the homepage URL, and the same pattern is used for other pages, as in the canonical URL block: `{% block canonical %}{{ url(routeName ?? null) }}{% endblock %}` (the `block` is present to handle not-found pages, e.g. mistyped URLs). +- An image referenced as `{{ url('home') }}images/app/My-image.png` is found at `public/images/app/My-image.png`, copied there by the `npm` script from `src/App/assets/images/PHP-REST-API.png`. -> In the example: -> -> - `{{ url('home') }}` is the URL for the homepage, but you can also use this code to generate the url for other pages, just like in the canonical URL `{% block canonical %}{{ url(routeName ?? null) }}{% endblock %}` -> - The `block` item is present to mitigate for not-found pages, e.g. when the url is typed incorrectly -> - The image from `{{ url('home') }}images/app/My-image.png` is found in `public/images/app/My-image``.png`, but it is copied there by the `npm` script from `src/App/assets/images/PHP-REST-API.png`. - -``` +```html @@ -123,9 +107,10 @@ Make sure to update all items based on your page content. ### Top menu -This menu is displayed on all of the pages, in the header. To edit it, go to `src/App/templates/layout/default.html.twig` and update the items under `id="navbarHeader"`. You can use the below as an example. +This menu is displayed on all pages, in the header. +Edit it in `src/App/templates/layout/default.html.twig`, under `id="navbarHeader"`: -``` +```html ``` -> You can also replace the `nav-item` class for the `li` elements with `button-border` for a link that looks more like a button. +You can replace the `nav-item` class for the `li` elements with `button-border` for a link that looks more like a button. ### Footer -To edit the footer on all of the pages, search for `