From a61cc1b36c8c8b7e22769c332008125d2eded523 Mon Sep 17 00:00:00 2001 From: OStefan2001 Date: Mon, 3 Aug 2026 23:08:16 +0300 Subject: [PATCH 1/3] Add opengraph img to article data. --- bin/update-path-opengraph-images | 118 +++++++++++++ src/App/src/Fixture/PostLoader.php | 12 +- src/App/src/Fixture/articles_cleaned.json | 165 +++++++++++++++++- .../src/Migration/Version20260803153306.php | 31 ++++ .../src/Migration/Version20260803200417.php | 31 ++++ src/App/src/Service/FeedGenerator.php | 5 +- src/App/templates/partial/meta.html.twig | 10 +- src/Blog/src/Entity/Post.php | 35 ++-- 8 files changed, 386 insertions(+), 21 deletions(-) create mode 100755 bin/update-path-opengraph-images create mode 100644 src/App/src/Migration/Version20260803153306.php create mode 100644 src/App/src/Migration/Version20260803200417.php diff --git a/bin/update-path-opengraph-images b/bin/update-path-opengraph-images new file mode 100755 index 0000000..0063377 --- /dev/null +++ b/bin/update-path-opengraph-images @@ -0,0 +1,118 @@ +#!/usr/bin/env php +get(EntityManager::class); +$postRepository = $entityManager->getRepository(Post::class); + +$config = $container->get('config'); +$baseUrl = rtrim($config['application']['baseUrl'] ?? '', '/'); + +/** + * + * @return array + */ +function indexUploadSources(string $uploadsDir, string $excludeDir): array +{ + $index = []; + + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($uploadsDir, FilesystemIterator::SKIP_DOTS) + ); + + foreach ($iterator as $file) { + if (! $file->isFile()) { + continue; + } + + $path = $file->getPathname(); + if (str_starts_with($path, $excludeDir . '/')) { + continue; + } + + $basename = $file->getFilename(); + if (! isset($index[$basename])) { + $index[$basename] = $path; + } + } + + return $index; +} + +$sourceIndex = indexUploadSources($uploadsDir, $ogDir); + +$postsUpdated = 0; +$postsSkipped = 0; +$dirsCreated = 0; +$imagesMissing = 0; + +$posts = $postRepository->findAll(); + +foreach ($posts as $post) { + $image = $post->getOpenGraphImage(); + if ($image === null || $image === '') { + continue; + } + + if ($baseUrl !== '' && str_starts_with($image, $baseUrl . '/')) { + continue; + } + + $filename = basename(parse_url($image, PHP_URL_PATH) ?: $image); + if ($filename === '') { + continue; + } + + if (! isset($sourceIndex[$filename])) { + printf("Source image '%s' not found for post '%s'%s", $filename, $post->getTitle(), PHP_EOL); + $imagesMissing++; + continue; + } + + $targetDir = $ogDir . '/' . $post->getId()->toString(); + if (! is_dir($targetDir)) { + if (! mkdir($targetDir, 0775, true) && ! is_dir($targetDir)) { + fwrite(STDERR, sprintf("Failed to create directory '%s'%s", $targetDir, PHP_EOL)); + continue; + } + $dirsCreated++; + } + + $targetPath = $targetDir . '/' . $filename; + if (! file_exists($targetPath) && ! copy($sourceIndex[$filename], $targetPath)) { + fwrite(STDERR, sprintf("Failed to copy '%s' to '%s'%s", $sourceIndex[$filename], $targetPath, PHP_EOL)); + continue; + } + + $post->setOpenGraphImage($baseUrl . '/uploads/opengraph/article/' . $post->getId()->toString() . '/' . $filename); + $postsUpdated++; +} + +$entityManager->flush(); + +$postsSkipped = count($posts) - $postsUpdated - $imagesMissing; + +printf( + "Done. %d post%s updated, %d director%s created, %d image%s missing, %d post%s skipped.%s", + $postsUpdated, + $postsUpdated === 1 ? '' : 's', + $dirsCreated, + $dirsCreated === 1 ? 'y' : 'ies', + $imagesMissing, + $imagesMissing === 1 ? '' : 's', + $postsSkipped, + $postsSkipped === 1 ? '' : 's', + PHP_EOL +); diff --git a/src/App/src/Fixture/PostLoader.php b/src/App/src/Fixture/PostLoader.php index 6f6b442..382a052 100644 --- a/src/App/src/Fixture/PostLoader.php +++ b/src/App/src/Fixture/PostLoader.php @@ -74,9 +74,10 @@ public function load(ObjectManager $manager): void ? new DateTimeImmutable($rawDate) : new DateTimeImmutable(); - $excerpt = $articleData['excerpt'] ?? ''; - $tlDr = $articleData['tl_dr'] ?? ''; - $isObsolete = (bool) ($articleData['isObsolete'] ?? false); + $excerpt = $articleData['excerpt'] ?? ''; + $tlDr = $articleData['tl_dr'] ?? ''; + $isObsolete = (bool) ($articleData['isObsolete'] ?? false); + $openGraphImg = $articleData['opengraph_img'] ?? null; $article = $repository->findOneBy(['slug' => $slug]); @@ -91,6 +92,7 @@ public function load(ObjectManager $manager): void $article->setExcerpt($excerpt); $article->setTldr($tlDr); $article->setObsolete($isObsolete); + $article->setOpenGraphImage($openGraphImg); $manager->persist($article); echo "CREATE: {$title}\n"; @@ -129,6 +131,10 @@ public function load(ObjectManager $manager): void $article->setObsolete($isObsolete); $changed = true; } + if ($article->getOpenGraphImage() !== $openGraphImg) { + $article->setOpenGraphImage($openGraphImg); + $changed = true; + } echo $changed ? "UPDATE: {$title}\n" : "UNCHANGED: {$title}\n"; } diff --git a/src/App/src/Fixture/articles_cleaned.json b/src/App/src/Fixture/articles_cleaned.json index 827a29f..da045a6 100644 --- a/src/App/src/Fixture/articles_cleaned.json +++ b/src/App/src/Fixture/articles_cleaned.json @@ -14,7 +14,8 @@ }, "excerpt": "Dotkernel borrows the database naming conventions from FaZend: Rules of naming of database tables and columns. FaZend is an open-source PHP framework based on Zend Framework.", "tl_dr": "Dotkernel's database naming conventions are borrowed from FaZend's \"Rules of naming of database tables and columns.\"\nTables 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.", - "isObsolete": false + "isObsolete": false, + "opengraph_img": null }, { "post_title": "camelCase Table Names in MySQL on Windows", @@ -25,6 +26,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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.", "tl_dr": "" }, @@ -37,6 +39,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dot_Email class extends Zend_Mail, so all the methods from Zend_Mail are available  in Dot_Email. Dot_Email is a simple class composed only from 2 methods, except constructor, all  other methods beeing inherited  from Zend_Mail.", "tl_dr": "Dot_Email extends Zend_Mail, so all of Zend_Mail's methods are available in it.\nBeyond its constructor, Dot_Email itself adds only two methods: setContent() and send().\nTo send an email you must always call addTo(), setSubject(), one of setBodyText()\/setBodyHtml()\/setContent(), and finally send()." }, @@ -49,6 +52,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "Finally we reached Dotkernel 1.2.", "tl_dr": "Dotkernel 1.2.0 has been released, bringing changes since the previous 1.1.2 release.\nThe 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.\nThe 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." }, @@ -61,6 +65,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "Yesterday, we released Dotkernel 1.2.", "tl_dr": "Dotkernel 1.2.2 is a bug-fix release that closes five tracked issues.\nBecause 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." }, @@ -73,6 +78,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "Dotkernel 1.3.", "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.\nBecause of that XML reorganization, 1.3.0 is not backward compatible with earlier versions." }, @@ -85,6 +91,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "GeoIP is the proprietary technology that drives MaxMind's IP geolocation data and services. It is a non-invasive way to determine geographical and other information about Internet visitors in real-time.", "tl_dr": "GeoIP is MaxMind's proprietary technology for IP geolocation.\nDotkernel uses it to get user statistics by country, determining a visitor's country, region, city, postal code, or area code in real time.\nThe logic lives in library\/Dot\/Geoip.php, inside the getCountryByIp function, which branches over four cases depending on whether the mod_geoip PECL extension and its .dat files are available." }, @@ -97,6 +104,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "WURFL is integrated into Dotkernel, using the Zend_Http_UserAgent class from the latest release ZF 1.11.", "tl_dr": "WURFL is integrated into Dotkernel using the Zend_Http_UserAgent class from ZF 1.11.0rc1 (the beta release at the time of the post).\nThis post walks through the required folders, config files, and code to wire it up." }, @@ -109,6 +117,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "Before the winter holiday we came with a new release: Dotkernel 1.3.", "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." }, @@ -121,6 +130,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Zend_Auth and Zend_Acl have been integrated into the Dotkernel, starting with version 1.5.", "tl_dr": "Zend_Auth and Zend_Acl have been integrated into Dotkernel starting with version 1.5.0.\nThe User and Admin models were completely refactored using the new Dot_Auth and Dot_Acl classes for authentication and access control." }, @@ -133,6 +143,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel has an example mobile site at http:\/\/v1.dotkernel.", "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.\nAs of revision 408, this behavior is controlled by a single resources.useragent.wurflapi.redirect setting in application.ini.\nThe article shows that setting along with the matching condition in IndexController.php that checks it before registering and redirecting a visit." }, @@ -145,6 +156,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In \/var\/www\/vhosts\/exampledomain.com\/conf\/vhost.", "tl_dr": "" }, @@ -157,6 +169,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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.", "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." }, @@ -169,6 +182,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In Dotkernel, Zend_Registry will contain the following variables: startTime - the result of microtime() at the beginning of the request configuration - the configuration options loaded from configs\/application.ini router - routing settings loaded from configs\/router.", "tl_dr": "In Dotkernel, Zend_Registry holds a fixed set of request-scoped variables — from timing and configuration to the database adapter and session object — and can be read either as a full instance or one value at a time." }, @@ -181,6 +195,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "We integrated long time ago the WURFL PHP API into Dotkernel code base. At that time, the license of that WURFL library was GNU\/GPL, which make it perfect compatible with Zend Framework license( new BSD) and Dotkernel ( OSL 3.", "tl_dr": "The WURFL PHP API was integrated into Dotkernel long ago under a GNU\/GPL license, compatible with Zend Framework's new BSD license and Dotkernel's OSL 3.0 license.\nOn June 6th, 2011, WURFL PHP API version 1.3.0 changed its license to AGPL, turning it into a \"trial only\" library for product evaluation.\nDotkernel had updated to this version in the 1.5.0 release candidate without noticing the license change." }, @@ -193,6 +208,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "After a longer wait than usual, Dotkernel 1.5.", "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.\nHighlights 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." }, @@ -205,6 +221,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "The new Dotkernel version 1.6.", "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.\nThe old Dot_UserAgent_Wurfl class was removed and replaced by Dot_UserAgent_WurflCloud, which uses the Wurfl Cloud API adapter.\nThe article walks through the application.ini settings and shows sample code for reading device info and redirecting mobile visitors." }, @@ -217,6 +234,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "We found a strange behaviour of sessions in one of our project, running Dotkernel version 1.5.", "tl_dr": "A strange session bug was found on a project running Dotkernel 1.5.0: in IE8 and IE9, the session cookie was sometimes not saved, forcing repeated logins.\nInvestigation traced it to the Dot_Session class calling both regenerateID() and rememberMe() unnecessarily, generating the session cookie 3 times.\nThe fix, shipped in Dotkernel 1.5.1, removed the regenerateID() call and added two new application.ini settings." }, @@ -229,6 +247,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Starting with 1.5, Dotkernel has a Console bootstrap to easily run PHP scripts from the command line.", "tl_dr": "Starting with version 1.5, Dotkernel has a Console bootstrap to easily run PHP scripts from the command line.\nThe most common use for this is running cron jobs without using wget or going through Apache." }, @@ -241,6 +260,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel Application Framework can be downloaded with WURFL XML file bundled in it, but is quite an old file,  the latest GPL version, from June 2011. Because of license changed of that WURFL file,  this bundled file will not be upgraded anymore by us.", "tl_dr": "Dotkernel Application Framework bundles a WURFL XML file, but it's the last GPL version (from June 2011).\nBecause of a license change to that WURFL file, Dotkernel will no longer upgrade the bundled file — it must be upgraded manually." }, @@ -253,6 +273,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "From time to time, it may be a good idea to have a persistent connection to database. The place where it should be added that new configuration option is application.", "tl_dr": "" }, @@ -265,6 +286,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Here is  uploaded the XML file , ready to be imported in your Zend Studio, version 9.x This file follow Dotkernel’s Coding standard.", "tl_dr": "" }, @@ -277,6 +299,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "According to Matthew Weier O'Phinney, Zend Framework Project Leader, in the next release of ZF, 1.12.", "tl_dr": "" }, @@ -289,6 +312,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In order to use UTF8 encoding in your Dotkernel based system, is needed to make some changes in both database structure and in the application.ini file.", "tl_dr": "To use UTF8 encoding in a Dotkernel-based system, changes are needed in both the database structure and the application.ini file.\nThese changes were committed into the Dotkernel 1.6.0 dev codebase." }, @@ -301,6 +325,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Another 2 of our team members passed the ZCE exam. Now we are 5 :-) That mean we are really taking PHP  into serious , and at least we have good technical skills.", "tl_dr": "" }, @@ -313,6 +338,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In some situations, it may be neccesar to force MySQL server collation and character set to UTF8. As you can't control all scripts that are connecting to your database( for instance: mysql command line, or mysqldump) For that , open the my.", "tl_dr": "In some situations it may be necessary to force the MySQL server's collation and character set to UTF8, since you can't control all the scripts connecting to your database (for instance the mysql command line or mysqldump).\nThis is done by editing my.cnf." }, @@ -325,6 +351,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "Integrating a new charting library in the latest version of Dotkernel (1.6.", "tl_dr": "Dotkernel 1.6.0 integrates the Highcharts charting library, offering a new, intuitive and interactive charting experience.\nSample charts (pie, column and line) were added to the admin, and the library ships in the project's externals directory." }, @@ -337,6 +364,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "Another new feature in version 1.6.", "tl_dr": "Dotkernel 1.6.0 integrates Wurfl Cloud, WURFL's (Wireless Universal Resource FiLe) new cloud-based way of delivering device detection services, as its default method for detecting mobile devices." }, @@ -349,6 +377,7 @@ "github": "arhimede" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "In the Dotkernel version 1.6.", "tl_dr": "In Dotkernel 1.6.0, released on May 16th, 2012, the GPL'ed WURFL PHP library was removed because its code was obsolete and the XML file structure had changed. It was replaced by Scientia Mobile's WURFL Cloud PHP library, made available to Dotkernel under a special, restrictive license." }, @@ -361,6 +390,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "According to Matthew Weier O'Phinney announcement, Zend Framework team is pleased to announce the immediate availability of the first release candidate of the Zend Framework 1.12 series, 1.", "tl_dr": "Per Matthew Weier O'Phinney's announcement, the Zend Framework team made available the first release candidate of the Zend Framework 1.12 series, 1.12.0RC1.\nIt back ports several ZF2 components to ZF1, removes the WurflApi adapter due to licensing changes, and fixes over 200 reported issues." }, @@ -373,6 +403,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Due to the fact that the current buzzword is Nginx instead of Apache, we decided to test if Dotkernel is running out of the box on it. And how to configure Nginx :-) Installed on a clean Centos 6.", "tl_dr": "Since Nginx was becoming the buzzword instead of Apache, this article tests Dotkernel on Nginx and documents the configuration needed: server block settings, a try_files directive in place of .htaccess, PHP-FPM handling, and protecting the configs folder." }, @@ -385,6 +416,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Because Zend donated the Zend Studio's Formatter upstream to the PDT project, the Formatter plugin for Zend Studio 10.1 need to be changed: replace  \"com.", "tl_dr": "" }, @@ -397,6 +429,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "To test if you have php_geoip extension on your Zend Server, create an php file and copy the following code. This will output true if extension is available or false if not.", "tl_dr": "Test whether php_geoip is already available, and if not, download the correct php_geoip.dll for your PHP build from windows.php.net, copy it into Zend Server's phpext folder, enable it from the Zend Server GUI, and download the MaxMind GeoIP databases." }, @@ -409,6 +442,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "As an update to the post Installing GeoIP extension in Zend Server 5.6 on Windows , for Zend Server 6.", "tl_dr": "As an update to Installing GeoIP extension in Zend Server 5.6 on Windows, here's how to enable php_geoip on Zend Server 6.1." }, @@ -421,6 +455,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In some cases you may encounter missing files: images, css or js files. All those missing files are processed by the current bootstrap: index.", "tl_dr": "" }, @@ -433,6 +468,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In order to use the new Password Hashing functions , introduced in PHP 5.5 , and unify all password related functions , used for both admin and users, we did a major refactor of Dotkernel codebase, in version 1.", "tl_dr": "To use the new Password Hashing functions introduced in PHP 5.5 and unify password-related functions for both admin and users, Dotkernel's codebase was refactored in version 1.8.0 (starting from revision 799).\nBecause those functions require PHP 5.5+, the Password Compat library is used for compatibility, and the minimum PHP version to run Dotkernel was raised to 5.3.8." }, @@ -445,6 +481,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article contains the Dotkernel cache layer configuration guide. The Dotkernel Caching Layer is based on Zend Framework Cache, more configuration options can be found at the following links: Zend Framework Cache Frontends Zend Framework Cache Backends Main cache settings (Cache Frontend) The main cache settings within the application.", "tl_dr": "Dotkernel's caching layer is built on Zend Framework Cache and is configured through cache.* settings in application.ini.\nThe main frontend settings control whether caching is enabled, which cache service to use, the namespace prefix, and how long entries live.\nOptional backend-specific settings (like the file cache directory) are recommended so that separate projects don't accidentally share the same cache." }, @@ -457,6 +494,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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.", "tl_dr": "Loading configuration and settings from XML files on every request is expensive, both due to hard-drive latency and XML parsing overhead.\nDotkernel 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." }, @@ -469,6 +507,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article is related to: Caching in Dotkernel with Zend Framework Cache The variables that Dotkernel cache are below: Router Router is the object that load routes (modules, controllers, actions) settings from router.xml file.", "tl_dr": "This article is a follow-up to \"Caching in Dotkernel Using Zend Framework Cache\" and lists the variables Dotkernel caches, along with the exact cache key each one uses." }, @@ -481,6 +520,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In the newest version we have removed the GeoIP City integration. The City database on GeoIP 1.", "tl_dr": "The newest Dotkernel version removed the GeoIP City integration because the City database on GeoIP extension version 1.1.0+ was causing a segmentation fault, crashing requests or outputting an error instead of executing the PHP code.\nUsers on an older Dotkernel version combined with GeoIP >=1.1.0 may hit this.\nIf you don't need GeoIP City, the affected code can be removed." }, @@ -493,6 +533,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Alerts (or Dot_Alert's) are e-mails usually sent to the site developers, these messages are sent with mail() therefore you shouldn't use them to send regular mail. Alerts should only notify you as a developer: \"Hey, something's wrong here, you might want to know this!\" In this article you will find out how to use the Alerts system in Dotkernel, we will also go through an existing example so this can be understood easier.", "tl_dr": "Alerts (Dot_Alert's) are e-mails usually sent to site developers using PHP's mail(), meant only to notify a developer that something is wrong — not for regular mail.\nDot_Alert resembles Dot_Email: it has a sender, subject, destination and message, and can be sent.\nThis guide walks through Dotkernel's existing example, where an Alert notifies the developer when an e-mail fails to send." }, @@ -505,6 +546,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "Dotkernel 1.8.", "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.\nSome features (WURFL integration, multiple SMTP transporters) were removed from core and made available as plugins instead." }, @@ -517,6 +559,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "Dotkernel 1.8.", "tl_dr": "Dotkernel 1.8.1 was released with Enhanced Cache Support, allowing cache tags to be used if the hosting environment supports them.\nA dedicated upgrade package is available for users coming from 1.8.0." }, @@ -529,6 +572,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Recently we have added the Windows 8, 8.1 and 10 OS icon and Microsoft's Edge browser icon.", "tl_dr": "Dotkernel added Windows 8, 8.1 and 10 OS icons and a Microsoft Edge browser icon, shown in the User and Admin login icons.\nThis article is the upgrade guide for applying that icon patch." }, @@ -541,6 +585,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Composer is an application-level package manager. Composer auto-loads the dependencies on demand and can also auto-load custom classes .", "tl_dr": "Composer is an application-level package manager that auto-loads dependencies (and custom classes) on demand.\nThis 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." }, @@ -553,6 +598,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article will cover the external dependency usage VIA composer within Dotkernel applications. There is also an article explaining how composer can be added to Dotkernel learn more.", "tl_dr": "This article covers using external dependencies via Composer within Dotkernel applications.\nComposer autoloads dependencies automatically, so there is no need to include\/require them.\nThe example renders a Barcode using Zend Framework 1 (non-namespaced) and Zend Framework 2 (namespaced), and applies to any Dotkernel 1.x version running PHP greater than 5.4.0." }, @@ -565,6 +611,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "The unofficial PEAR channel for Zend Framework 1 was hosted on Google Code at this location: ZF Pear, but since the closing of Google Code we were forced to move it. Zend Framework 1 is still used by a lot of  projects in Production, it's still a viable library collection  and it's also  running on  PHP7 ; even if is only in maintenance\/security-patch mode, so it's not an option to cancel it completely.", "tl_dr": "The unofficial PEAR channel for Zend Framework 1 was hosted on Google Code, and once Google Code closed, it had to move.\nBecause the repository is over 1 GB, it could not be migrated to GitHub, so a dedicated server was built to host the PEAR channel long-term at pear.dotkernel.com." }, @@ -577,6 +624,7 @@ "github": "" }, "isObsolete": true, + "opengraph_img": null, "excerpt": "What is Dotkernel? The name Dotkernel symbiotically  combines the string  Dot, as a representation of the Internet, and Kernel, the quintessence of any IT application. In other words Dotkernel wishes to be, with modesty, the central part of the Internet development and hence ensuring increased development productivity and run-time performance.", "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.\nDotkernel 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.\nSince Dotkernel 3's release, the unqualified name \"Dotkernel\" refers to Dotkernel 3, while Dotkernel 1 is always referenced explicitly." }, @@ -589,6 +637,7 @@ "github": "jesper@apidemia.dk" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel3 aims to improve the Dotkernel stack in every way possible, and one of the painpoints in the previous version of Dotkernel was the templating engine. Albeit a solid and robust templating engine, it was also 10 years old, and used techniques that's slightly outdated by now.", "tl_dr": "Dotkernel3 moved from its previous, 10-year-old templating engine to the popular Twig Templating Engine, gaining layouts, loops, variables, and escaping, while giving developers the familiarity of HTML with the overview and convenience of PHP." }, @@ -601,6 +650,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article will explain the usage of the dot-log component within Dotkernel, Zend Expressive or in a project that uses Zend Service Manager. Since dot-log extends zendframework\/zend-log this tutorial mostly compatible with zend-log as well.", "tl_dr": "This article explains how to use the dot-log component within Dotkernel, Zend Expressive, or any project that uses Zend Service Manager.\nSince dot-log extends zendframework\/zend-log, the tutorial is mostly compatible with zend-log as well.\nSee the zend-log documentation for more detail." }, @@ -613,6 +663,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article is a follow-up for: Logging with dot-log in Zend Expressive and Dotkernel, the mentioned article is a guide to using dot-log.   This article explains the usage of dotkernel\/dot-errorhandler with dotkernel\/dot-log or zendframework\/zend-log to log errors in Zend Expressive applications.", "tl_dr": "This article is a follow-up to \"Logging with dot-log in Zend Expressive and Dotkernel\" and explains how to use dotkernel\/dot-errorhandler together with dotkernel\/dot-log or zendframework\/zend-log to log errors in Zend Expressive applications.\nIt covers how dot-errorhandler was built, how to configure it, and how it was tested." }, @@ -625,6 +676,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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.", "tl_dr": "When a client-side request is blocked with a \"No 'Access-Control-Allow-Origin' header\" error, it's because the server isn't sending the header that allows a browser to access its data (most common when fetching JSON to process with JavaScript).\nThis guide adds CORS support to a Zend Expressive \/ Dotkernel3 project using Tuupola's Cors Middleware package." }, @@ -637,6 +689,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel will be a \"skeleton\"of Zend Framework. Dotkernel borrowed the coding standard from Zend Framework: ZF Coding Standard with some exceptions.", "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." }, @@ -649,6 +702,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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.", "tl_dr": "Dotboost announces its North American relaunch, aimed at better serving clients in Canada and the US.\nThe relaunch centers on the source release of its in-house Dotkernel framework, along with expanded business IT integration and clearer consulting services.\nFounded in 2005, Dotboost describes itself as treating clients as strategic partners rather than as a typical IT vendor." }, @@ -661,6 +715,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel is the DotBoost's in-house developed framework, based on Zend Framework. Dotkernel is at version 1.", "tl_dr": "Dotkernel is DotBoost's in-house developed framework, built on top of Zend Framework and released under the Open Software License (OSL 3.0).\nIt uses a simplified MVC architecture, easy to learn for beginner and intermediate programmers, by eliminating much of Zend Framework's complexity through a different approach to handling web requests.\nIt relies on only a handful of Zend Framework classes." }, @@ -673,6 +728,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel Template Engine is an implementation of PHPLib Template engine for PHP5. It has an amazing ability to separate the application code from the presentation layer.", "tl_dr": "" }, @@ -685,6 +741,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "As described in this article, dot-log is a powerful tool for logging messages in your application. It's power stays in the fact that it can be implemented in a few easy steps and that it's highly customizable.", "tl_dr": "dot-log is a powerful, easily customizable logging tool.\nVersion 3.1.1 adds the ability to use datetime formatter strings right in the stream option of a log writer, and fixes an issue where caching dot-log configs caused logs to be written to a single file instead of being grouped by date." }, @@ -697,6 +754,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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 starting from Release 3.", "tl_dr": "This feature automatically logs in a user who checks the \"remember me\" box at login.\nIt has been implemented in 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." }, @@ -709,6 +767,7 @@ "github": "marioradu05" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/02\/doctrine-cache-using-symfony-cache-small.png", "excerpt": "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.", "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.\nThis 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.\nIt covers the array and filesystem storage adapters, configuring Doctrine's four cache types (result, metadata, query, hydration), and marking entities and queries as cacheable." }, @@ -721,6 +780,7 @@ "github": "claudiu@rospace.com" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/06\/twitter-card.png", "excerpt": "Note: The package requires Doctrine ORM. Still, it can be used in applications which do not integrate Doctrine.", "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.\nInstead of a bespoke factory, you add an attribute to the class constructor and register a single shared AttributedServiceFactory in your ConfigProvider.\nThe 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." }, @@ -733,6 +793,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/10\/dotkernel-light-starting-with-mezzio-microframework-and-laminas-components.png", "excerpt": "Dotkernel Light is a version of Dotkernel Frontend that includes only the bare-bones essentials. Though simpler, 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.", "tl_dr": "Dotkernel Light is a version of Dotkernel Frontend that includes only the bare-bones essentials.\nIt'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." }, @@ -745,6 +806,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/10\/twitter-card-Light-choice.png", "excerpt": "Dotkernel Light is a good starting point for a project if you want to have full control over the functionality it contains. It easily grows into something more complex with the integration of packages based on your requirements.", "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.\nIt 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." }, @@ -757,6 +819,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/11\/twitter-card-enum.png", "excerpt": "The update of doctrine\/orm to version 3.2.", "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.\nThe 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.\nThe 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." }, @@ -769,6 +832,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/01\/twitter-card-symfony-mailer.png", "excerpt": "What prompted the change According to the discussion from the LaminasTechnical steering Committee of 2023-12-04, it was decided that the laminas\/laminas-mail package would be abandoned. On the one hand, there is nobody to maintain the package and on the other, there are several alternatives available in the ecosystem: ddeboer\/imap for interacting with IMAP zbateson\/mail-mime-parser for parsing MIME messages symfony\/mailer for sending mail How Dotkernel handles the issue The Dotkernel team has also opted to replace the laminas\/laminas-mail package in the dotkernel\/dot-mail package.", "tl_dr": "The Laminas Technical Steering Committee decided on 2023-12-04 to abandon laminas\/laminas-mail.\nDotkernel responded by replacing it with symfony\/mailer inside the dotkernel\/dot-mail package (version 5), aiming for minimal impact on existing projects — calls to send mail stay the same, though mime and imap related functionality is removed." } @@ -788,6 +852,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Starting with the 1.5 release, Dotkernel will make the switch from Dojo to jQuery.", "tl_dr": "Starting with Dotkernel's 1.5 release, the framework switched from Dojo to jQuery, and this post is a quick primer on jQuery basics.\nIt covers the jQuery ($) object and CSS-style selectors, chaining methods to manipulate matched elements, binding events like click, and making Ajax calls with $.get() and $.getJSON()." }, @@ -800,6 +865,7 @@ "github": "stas@codelobster.com" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Free PHP, HTML, CSS, JavaScript editor (IDE) - Codelobster PHP Edition For valuable work on creation of sites you need a good comfortable editor necessarily. There are many requiring paid products for this purpose, but we would like to select free of charge very functional and at the same time of simple in the use editor - Codelobster PHP Edition .", "tl_dr": "Codelobster PHP Edition is a free, lightweight IDE that highlights and autocompletes mixed PHP, HTML, CSS, and JavaScript code, including HTML5 and CSS3.\nIt also bundles an HTML\/CSS inspector, a PHP debugger, an SQL manager, FTP support, and a portable mode that needs no installation.\nOn top of that, it ships plugins for popular CMS platforms and PHP frameworks such as Drupal, Joomla, CakePHP, CodeIgniter, Symfony, Yii, WordPress, and Smarty." }, @@ -812,6 +878,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Problem: email should allow +\/- characters in user, - in domain. dash (-) should be allowed anywhere in an email address or domain.", "tl_dr": "" } @@ -831,6 +898,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "GPL versions of WURFL PHP API libraries are ready to be downloaded from here . Version 1.", "tl_dr": "GPL versions of the WURFL PHP API libraries were made available: version 1.1, the one integrated into Zend Framework's Zend_Http_UserAgent component, and version 1.2.1, the latest released under the GPL license.\nA later edit notes the download was removed because an AGPL version is available (which readers need to get themselves), and as a favor to Luca Passani." }, @@ -843,6 +911,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Matthew Weier O'Phinney just announced the release of ZF 1.12.", "tl_dr": "Matthew Weier O'Phinney announced the release of Zend Framework 1.12.4, along with 2.1.6 and 2.2.6, all containing security updates, and the ZF PEAR channel was updated to the latest 1.12.4 release.\nA March 7, 2014 edit notes that Zend Framework 1.12.5 was subsequently released to fix a backward compatibility issue introduced in the 1.12.4 release." }, @@ -855,6 +924,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "The release of ZF 1.12.", "tl_dr": "Zend Framework 1.12.12 was released with security fixes for the Zend_Mail and Zend_Http components.\nConsumers of these components, including Dotkernel which relies heavily on Zend_Mail, were strongly urged to upgrade immediately via PEAR or by applying the patch directly.\nA follow-up release, 1.12.13, was issued shortly after to fix a regression introduced in 1.12.12." }, @@ -867,6 +937,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "EOL ( End-of-Life)  term was just announced. Only up until Sept.", "tl_dr": "Zend Framework 1 has officially entered End-of-Life (EOL) status now that Zend Framework 3 has been released.\nSecurity updates for Zend Framework 1 continued only until 28 September 2016, three months after the announcement." }, @@ -879,6 +950,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Why we want to install ZF as PEAR ? Because is too boring and time consuming to move all ZF files up and down for each script you want to install , there are a lot of files. Also that way we can forget about the need to update ZF at latest versions, and keep tracks of which version and on which server we have ZF.", "tl_dr": "Rather than copying all of Zend Framework's many files into every project, this article shows how to install ZF as a PEAR-accessible repository on a Plesk server.\nThis makes it easier to track which ZF version is installed on which server and avoids manually updating each project, though it can introduce backward compatibility concerns in future ZF releases." }, @@ -891,6 +963,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "At first glance , the biggest news is AMF support: Adobe's Action Message Format protocol to your PHP 5 application Download latest  ZF", "tl_dr": "Zend Framework 1.7.0 has been released, and its headline feature is support for Adobe's Action Message Format (AMF) protocol in PHP 5 applications.\nThe release is available directly from the official Zend Framework download page." }, @@ -903,6 +976,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Just found today a very interesting and helpful debug tool: Scienta We at Dotkernel used some very basic debug bar:  queries, time spent , memory used.  But this Scienta is way more complex and nicer then our internal code,  so we switch to it and integrate it in Dotkernel code base.", "tl_dr": "The author came across the Scienta ZF Debug Bar, a debugging tool for Zend Framework applications.\nDotkernel had been relying on its own basic debug bar, which only showed queries, time spent, and memory used.\nFinding Scienta far more complex and polished than their internal tool, the Dotkernel team decided to switch to it and integrate it into the Dotkernel code base." }, @@ -915,6 +989,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Sunsetting PEAR Channel for Zend Framework 1 The unofficial PEAR channel for Zend Framework 1 was created in 2016 , at the time when PEAR was still used a lot. Due to the fact that is a pain to upgrade PEAR to work with PHP 8 , we must sunset the channel .", "tl_dr": "Dotkernel is sunsetting its unofficial PEAR channel for Zend Framework 1, which was created in 2016 when PEAR was still widely used.\nThe main reasons are that upgrading PEAR to work with PHP 8 is too painful, and the channel currently runs on an LXC container with CentOS 7, which doesn't work on the latest Proxmox version, making the upgrade to AlmaLinux not worth the effort.\nThe post closes by thanking PEAR for its historical contribution to the PHP ecosystem." } @@ -934,6 +1009,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Aptana 3.0 is in beta stage, can be downloaded from the official site .", "tl_dr": "Aptana 3.0, then in beta, was set to bring PHP support back - and this time it would be built directly into the Studio 3 core rather than shipped as a separate plugin.\nA PHP debugger was also announced, to arrive as a separate set of plugins a few weeks later." }, @@ -946,6 +1022,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "SQL injection is a technique that exploits a security vulnerability occurring in the database layer of an application. Usually, user input is not filtered by the script and is passed into a SQL statement.", "tl_dr": "SQL injection exploits unfiltered user input passed into SQL statements.\nPDO (PHP Data Objects) is a standardized database access layer that provides a data-access abstraction (not a database abstraction) and offers several benefits, including help protecting against SQL injection.\nIn Zend Framework, prepared statements are encouraged since they handle parameter escaping, but they are not a complete guarantee against SQL injection - especially with PDO_MySQL, and with WHERE IN \/ ORDER BY clauses." }, @@ -958,6 +1035,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Following the preview article about SQL Injection, here is more - a strong argument why you should use Zend Framework for handling database access. Zend_Db is the primary class used for access the database, but there is more: Zend_Db_Statement, Zend_Db_Select and Zend_Db_Tables.", "tl_dr": "Following up on the earlier SQL Injection article, this part digs into the specific methods of Zend_Db (and related classes Zend_Db_Statement, Zend_Db_Select, Zend_Db_Tables) to show exactly when their use of prepared statements does, and does not, protect against SQL injection - and offers a quick type-casting tip for WHERE clauses." }, @@ -970,6 +1048,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "PHP 5.2.", "tl_dr": "PHP 5.2.14 was just released, marking the end of active support for the PHP 5.2.x branch.\nPHP 5.3.3 was released at the same time, and projects and servers are encouraged to upgrade to the 5.3.x branch." }, @@ -982,6 +1061,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In hosted software development, the environment refers to a server tier designated to a specific stage in a release process. The purpose of these environments is to improve the development, testing and release processes in client-server applications.", "tl_dr": "In hosted software development, an environment is a server tier designated to a specific stage of a release process.\nThe three most common environments are Development, Staging and Production, and applications are typically moved between them using Subversion source control." }, @@ -994,6 +1074,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In developing Dotkernel application framework, we needed a tracking system. Dotkernel Tracker is the place where the bugs are reported, new features are announced and other general tickets are added.", "tl_dr": "This guide explains how to connect the Aptana IDE to Dotkernel Tracker, the Mantis-based bug tracker used for the Dotkernel application framework, via the Mylyn plugin's Mantis connector.\nIt walks through installing Aptana and Mylyn, adding Dotkernel Tracker as a task repository, and validating the connection so tickets can be managed directly from the IDE." }, @@ -1006,6 +1087,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "1.    Stop WAMP server.", "tl_dr": "A step-by-step guide to manually upgrading the PHP version used by a WAMP server to PHP 5.3.4, by downloading the VC6 Thread Safe build, copying over configuration files, and switching the active PHP version in WAMP." }, @@ -1018,6 +1100,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "PHP 5.3.", "tl_dr": "" }, @@ -1030,6 +1113,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In order to make  usable a fresh installation of Zend Server 5.5.", "tl_dr": "A fresh Zend Server 5.5.0 install on Windows 7 needs a few quick tweaks before it's ready for development: enabling mod_rewrite in Apache, adjusting a handful of PHP directives, and fixing APC so it actually works even though it's shown as enabled." }, @@ -1042,6 +1126,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "By default, MySql servers on Linux machines where Plesk is installed, have the old_passwords=1 or ON flag. That mean even if you have MySQL 5.", "tl_dr": "Plesk-based Linux servers default to old_passwords=1, which forces MySQL to use the old, pre-4.1 password storage style even on MySQL 5.5+, breaking remote PDO connections.\nThe fix is to create a new database user, grant it privileges, disable old_passwords, and reset its password so a newer, longer password hash is stored in mysql.user." }, @@ -1054,6 +1139,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In order to globally manage the \"Ignored Resources\"  patterns in Zend Studio, for all projects , instead of manually add to each project, you can do the following: 1. Go to Window-> Preferences 2.", "tl_dr": "Zend Studio lets you manage \"Ignored Resources\" patterns globally, under Window -> Preferences -> Team -> Ignored Resources, instead of configuring them separately for each project.\nThis is especially handy when a workspace mixes Git and SVN projects, though any given project can still opt to use its own specific patterns instead of the global ones." }, @@ -1066,6 +1152,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Today is a major milestone for our Dotboost Technologies Inc. Company.", "tl_dr": "Dotboost Technologies Inc. announces that the 10th member of its team has passed the Zend Certified Engineer exam, part of its commitment to top-level PHP development and quality assurance for clients.\nNext up: adopting Zend Framework 2 best practices, pursuing the Zend Framework 2 Certified Architect exam, and, starting in 2014, making Zend Certification mandatory for every developer on the team." }, @@ -1078,6 +1165,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Beginning with version 5.5 of MySQL , utf8mb4 character set was introduced, in order to better support Unicode.", "tl_dr": "MySQL 5.5 introduced the utf8mb4 character set for fuller Unicode support, and Dotkernel's sample dk.sql file was updated to use it.\nSwitching to utf8mb4 means VARCHAR(255) columns can hit MySQL's 767-byte max key length error, so VARCHAR(150) is used instead, and the connection charset must be updated in both the application config and my.cnf." }, @@ -1090,6 +1178,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article will cover the steps needed in order to check the PHP7 compatibility, a small troubleshooter. This article will also contain a compatibility issue check on the latest Zend Framework 1 version.", "tl_dr": "Zend Studio 13 introduces PHP 7 Express, a feature that checks whether pre-PHP7 code will run cleanly on a PHP7 server.\nThis article walks through setting up a test project with the correct PHP version, verifying the PHP Interpreter setting, adding Zend Framework 1 to the project, and running PHP 7 Express to surface compatibility issues." }, @@ -1102,6 +1191,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article applies to PHP 5.x but also to PHP 7 While using floating-point arithmetic you might have noticed that not all the calculus results are as expected, this can usually be observed when casting values.", "tl_dr": "This applies to PHP 5.x and PHP 7.\nFloating-point arithmetic doesn't always produce the results you'd expect, especially when casting values to int, because numbers like 0.7 and 0.1 cannot be represented exactly in binary.\nThe result is that (int)((0.7+0.1)*10) evaluates to 7 instead of the mathematically expected 8." }, @@ -1114,6 +1204,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "As all aptana fans know, Aptana PHP plugin was discontinued in Aptana 2.x, in favor of PDT.", "tl_dr": "Aptana discontinued its bundled Aptana PHP plugin in Aptana 2.x in favor of PDT, but PDT is missing major features needed for professional PHP development.\nThis article shows how to manually reinstall the Aptana PHP plugin through Aptana's update site, and how to add SVN support via Subclipse if it isn't already installed." }, @@ -1126,6 +1217,7 @@ "github": "marioradu05" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Database seeding: Doctrine data fixtures vs Phinx Seeding the database means populating the database with initial values, it's commonly used for seeding the user roles and user accounts. Seeding the database the right way is no easy feat, and we will see why.", "tl_dr": "Dotkernel 3 previously used cakephp\/phinx for seeding the database, but the team wanted more flexibility and switched to doctrine\/data-fixtures since Doctrine is already the ORM in use.\nBecause doctrine\/data-fixtures has no CLI interface, Dotkernel built the dotkernel\/dot-data-fixtures package to add one, and this article covers installing it, creating and executing fixtures, and ordering them by explicit order or by declared dependencies." }, @@ -1138,6 +1230,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Install a Mezzio app (Dotkernel API) using WSL2 This article will run you through the steps of installing a Mezzio application (Dotkernel API) in WSL2 and run it on Ubuntu 20.04 LTS.", "tl_dr": "This article runs through the steps of installing a Mezzio application (Dotkernel API) in WSL2 and running it on Ubuntu 20.04 LTS, from installing WSL2 itself to configuring PHPStorm to work with the WSL2 file system." }, @@ -1150,6 +1243,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2022\/12\/wsl2-php-dotkernel.png", "excerpt": "In this article we will demonstrate how we install AlmaLinux 9 using Windows Subsystem for Linux (WSL2). First, you need to check if your machine is ready for using WSL2.", "tl_dr": "This guide shows how to install AlmaLinux 9 through Windows Subsystem for Linux (WSL2) and provision it with an Ansible-driven installer script that sets up PHP, Apache, MariaDB, Composer, and phpMyAdmin.\nIt covers verifying WSL2 readiness, installing the AlmaLinux 9 distribution from the Microsoft Store, running the two-step Ansible installer (with a required restart in between), and confirming the setup through Apache's homepage, a PHP info page, and phpMyAdmin." }, @@ -1162,6 +1256,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/11\/twitter-card-static-analysis.png", "excerpt": "What is Static Analysis Static analysis (static code analysis or source code analysis) applies a set of coding rules to debug source code before a program is run. Applied in the early phase of code development, the goals of static analysis are: Catch and fix errors like type-related errors which can occur especially in dynamically-typed programming languages like PHP.", "tl_dr": "Dotkernel is replacing Psalm with PHPStan for static analysis, following a broader PHP community shift (including projects like Doctrine and Composer) toward PHPStan's faster-growing ecosystem, full-time maintainer, PHPStorm-based stubs, and stronger detection.\nThis article explains what static analysis is, why the switch makes sense, and walks through updating composer.json, the CI workflow, and the phpstan.neon configuration to run PHPStan checks in place of Psalm." } @@ -1181,6 +1276,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "What is PSR-7 and how to use itPSR-7 is a set of common interfaces defined by PHP Framework Interop Group. These interfaces are representing HTTP messages, and URIs for use when communicating trough HTTP.", "tl_dr": "PSR-7 defines a set of common interfaces from the PHP Framework Interop Group for representing HTTP messages and URIs, and any application built on those interfaces is a PSR-7 application.\nThis article lists the PSR-7 interfaces as a cheatsheet, then walks through practical examples using Zend Diactoros: adding, appending, reading, and removing HTTP headers, and reading, writing, appending, and prepending content to a PSR-7 message body via its stream interface." }, @@ -1193,6 +1289,7 @@ "github": "jesper@apidemia.dk" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Migrations, the superhero your database deserves Migrations ease the process of working together on projects, as well as deploying the database changes.   A newly released package for the Dotkernel stack integrates migrations and seeders into the application; This is all done via the newly introduced \"php dot\" command that's available in the Dotkernel stack.", "tl_dr": "Database migrations track schema changes so teams can collaborate without ad hoc, convoluted database change messages and can keep column types consistent across the team.\nA package for the Dotkernel stack adds migrations and seeders to the application via a new php dot command.\nThe article walks through adopting migrations in an existing project, running and naming them, and explains how seeders differ from migrations by adding data rather than changing schema." }, @@ -1205,6 +1302,7 @@ "github": "jesper@apidemia.dk" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel 3 uses FastRoute under the hood, which is an excellent and fast routing package, but it does have some quirks. A wrong setup can lead to many headaches, as it's not prominent that the error you're experiencing is from FastRoute, and you may not know where exactly to look for the cause.", "tl_dr": "Dotkernel 3 uses FastRoute under the hood, which is fast but has a quirk around slash-suffixes that a wrong route setup can trigger, leading to hard-to-diagnose errors.\nOptional slashes must be kept inside the optional block of a route definition, or the URLGenerator ends up producing the wrong route.\nEvery named route can then be referenced instead of hard-coded, using $this->url() in controllers or the path() function in Twig views." }, @@ -1217,6 +1315,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article covers the steps required to migrate a Dotkernel 3 instance to the latest Zend Expressive Version. Migration from Zend Expressive 2 to 3.", "tl_dr": "This guide covers migrating a Dotkernel 3 instance from Zend Expressive 2 to Zend Expressive 3, for projects that only contain controller-based middleware.\nOld middleware must first be refactored to the psr\/http-server-middleware interfaces, since Delegates become RequestHandlers.\nThe steps then cover updating composer.json dependencies, registering new ConfigProviders, wrapping routes.php and pipeline.php in callables, and replacing the old pipeRoutingMiddleware()\/pipeDispatchMiddleware() calls with their PSR-15 equivalents." }, @@ -1229,6 +1328,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Doctrine caching in Dotkernel Following version 2 of doctrine\/cache, in 2024 we published an update to this article here: https:\/\/www.dotkernel.", "tl_dr": "Running Doctrine ORM in production without any caching strategy wastes CPU cycles regenerating metadata and queries on every request.\nThis article configures Doctrine's metadata_cache, query_cache, and result_cache through psr\/container, using PhpFileCache and a default result cache lifetime of 3600 seconds.\nIt walks through enabling these caches both directly on a query and on a Doctrine Paginator-based collection, with real examples from Dotkernel Admin.\nNote: a 2024 follow-up article covers the same topic using Symfony Cache instead." }, @@ -1241,6 +1341,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "CORS policy setup in Dotkernel using mezzio-cors Error message Access to fetch at RESOURCE_URL from origin ORIGIN_URL has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Most developers have encountered this error when interacting with APIs.", "tl_dr": "This article explains how to fix the common \"No 'Access-Control-Allow-Origin' header is present\" browser error by installing and configuring the mezzio-cors package.\nIt covers registering the package's ConfigProvider and middleware, then creating a CORS configuration file.\nThe configuration supports a permissive mode, where any origin is allowed, and a restrictive mode, where only specific listed origins are allowed.\nIt also shows how to verify each mode is working correctly." }, @@ -1253,6 +1354,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Replacing dot-console with dot-cli based on laminas-cli Implementing dot-cli in your application Dotkernel's dot-cli package comes as a replacement for dot-console, which was abandoned after Laminas abandoned their laminas-console package, that dot-console was based on. Setup Install package Run the following command in your application's root directory: composer require dotkernel\/dot-cli Register ConfigProvider Open your application's config\/config.", "tl_dr": "Dotkernel's dot-cli package replaces dot-console, which was abandoned after Laminas dropped the laminas-console package it was based on.\nSetting it up involves requiring the package via Composer, registering its ConfigProvider, and copying its bootstrap and config files into the application.\nIt also ships with FileLocker, a built-in, enabled-by-default locking system that prevents overlapping calls to the same command." }, @@ -1265,6 +1367,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Displaying Dotkernel API endpoints using dot-cli Starting from version 3, Dotkernel API uses dot-cli to display a list of available endpoints. Usage Run the following command in your application’s root directory: php .", "tl_dr": "Starting from version 3, Dotkernel API uses the dot-cli package to list all of its available endpoints via the route:list command.\nThe command's output can be filtered by route name, path, or HTTP method, and filters are case-insensitive and combinable." }, @@ -1277,6 +1380,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Creating admin accounts in Dotkernel API Starting from v3, Dotkernel API introduces support for admin accounts. In this article we will describe two different methods of creating an admin account.", "tl_dr": "Starting with version 3, Dotkernel API supports dedicated admin accounts.\nThey can be created either through a protected API endpoint, which lets you assign one or more admin roles and optional names, or through a terminal command, which is quicker but always assigns the default admin role.\nBoth methods leave you with a ready-to-use admin account." }, @@ -1289,6 +1393,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Using Postman documentation in Dotkernel API 3 Starting from version 3.0 Dotkernel API provides it's documentation using Postman.", "tl_dr": "Starting from version 3.0, Dotkernel API documents its endpoints using Postman, via a provided collection and environment file that get imported into the tool.\nPostman organizes work into a Workspace, Collections, Environments, and Requests, and the Dotkernel API collection ships with built-in security: global Bearer Token authorization inherited from the collection root, and automatic ACCESS_TOKEN\/REFRESH_TOKEN storage on the Admin\/Security and User\/Security folders.\nAfter making changes, the collection and environment files can be re-exported to overwrite the application's documentation files." }, @@ -1301,6 +1406,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Generating a doctrine migration without dropping custom tables If your application needs to hold some custom (unmapped) tables in the database, then generating migrations with doctrine-migrations diff will try to drop the custom tables. This article provides a solution on how to avoid dropping those tables.", "tl_dr": "When an application has custom, unmapped database tables, running doctrine-migrations diff will try to drop them, since no Doctrine entity describes them.\nThis article shows how to prevent that using the --filter-expression option, including how to filter multiple table prefixes at once.\nIt also flags a Windows PowerShell quirk where the caret in the regex gets stripped, and how to work around it." }, @@ -1313,6 +1419,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "What is cross origin token redemption? Cross-origin token redemption is a technique used to ensure the security and authenticity of a token that is issued by one website or domain, but intended for use on a different website or domain. This process is commonly used in situations where a user needs to access resources from multiple domains, such as when a user is logged in to one website and needs to access resources from another website.", "tl_dr": "Cross-origin token redemption verifies the security and authenticity of a token issued on one domain but used on another, which is common when a logged-in user needs to access resources on a different site.\nThe receiving domain checks the token's signature and decrypts it before trusting it.\nJWT and OAuth 2.0 are two standards that implement this pattern, each with a different verification flow." }, @@ -1325,6 +1432,7 @@ "github": "marioradu05" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Prerequisites: Mezzio App Doctrine In the vast digital landscape of the internet, where websites compete for attention, having a well-crafted URL can make a significant difference. By incorporating human-readable slugs into website URLs, we can enhance user experience, improve search engine optimization (SEO), and foster better engagement.", "tl_dr": "Human-readable URL slugs improve readability, SEO, and shareability compared to raw numeric IDs in URLs.\nThis article shows how to add slug support to a Mezzio application using the gedmo\/doctrine-extensions package.\nIt covers installing the package via Composer, registering its SluggableListener with Doctrine, and adding a slug column generated from an existing field (such as identity) via the @Gedmo\\Slug annotation." }, @@ -1337,6 +1445,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/06\/twitter-card-almalinux10.png", "excerpt": "With the recent release of AlmaLinux OS 10, we have created a new recipe for our WSL development environment. Compared to its predecessor, AlmaLinux 10 provides performance enhancements, security updates and improved hardware support.", "tl_dr": "With the release of AlmaLinux OS 10, Dotkernel created a new WSL2 development environment recipe offering performance, security, and hardware improvements over AlmaLinux 9.\nThe recipe sets up WSL2, AlmaLinux 10, PHP, Apache, MariaDB, Git, Composer, Node.js, and PhpMyAdmin.\nIt also covers the OS\/hardware requirements, installing the distro, and running PHP projects directly or via virtual hosts." } @@ -1356,6 +1465,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Problem PHP packages\/frameworks\/libraries\/scripts we work with might require different PHP extensions. In this case the Intl extension is needed to work with using Internationalization Functions.", "tl_dr": "Errors like \"requires intl PHP extension\" or \"extension intl is missing\" happen because the PHP Intl extension isn't installed or enabled.\nThis article explains what Intl is used for, why it might be missing depending on whether you have a bundled or unbundled PHP install, and gives step-by-step fixes for both Linux and Windows servers." }, @@ -1368,6 +1478,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article will cover the solution to the PEAR \"Cannot use result of built-in function in write context\" issue. The Issue If installing a pear package (for instance PHP Code Sniffer), when running: pear install PHP_CodeSniffer This error is shown PHP Fatal error: Cannot use result of built-in function in write context in .", "tl_dr": "On PHP 7.2, installing PEAR packages such as PHP Code Sniffer fails with a \"Cannot use result of built-in function in write context\" error in Archive_Tar's Tar.php, because a function is called by reference.\nThe fix is to edit the offending line in Tar.php to drop the by-reference call, then reinstall Archive_Tar and the target package." } @@ -1387,6 +1498,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This report contains updates about the Dotkernel3 documentation. We have added the release notes for Dotkernel3 frontend and admin: you can now check the Release Notes page.", "tl_dr": "This report covers updates to the Dotkernel3 documentation: new release notes for the frontend and admin, a Webpack tutorial added to the Prerequisites section, and revisions to the Api Endpoint Documentation Guidelines.\nContributor JapSeyz is thanked for this round of updates." }, @@ -1399,6 +1511,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel was updated to support Zend Expressive 3 alongside with PSR-15 middleware. We have updated the core packages to support PSR-15 Middleware.", "tl_dr": "Dotkernel3 1.0 updates the core packages to support Zend Expressive 3 and PSR-15 middleware, making both frontend (1.0.0) and admin (1.0.1) easier to migrate.\nNo functional changes were made to the core code, though projects using the old http-interop\/http-middleware package must migrate to the psr\/http-server-middleware interfaces.\nExisting Dotkernel 3 (Expressive 2) projects can follow a separate guide to move to Zend Expressive 3." }, @@ -1411,6 +1524,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel Frontend PHP Application version 3 was launched. Dotkernel is a Collection of PSR-7 Middleware applications built on top of Mezzio microframework and using Laminas components You can clone it from github Live demo: v3.", "tl_dr": "Dotkernel Frontend version 3 has launched as part of the Dotkernel collection of PSR-7 Middleware applications, built on the Mezzio microframework using Laminas components.\nThe source is available on GitHub, with a live demo running at v3.dotkernel.net.\nBranch 3.0 is now the default branch, requiring Mezzio ^3.2, PHP ^7.4, Doctrine 2.7.x, and Twig 3.x, with dot-* packages limited to version 3.x and above." }, @@ -1423,6 +1537,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel Admin PHP Application version 3 was launched. Dotkernel is a Collection of PSR-7 Middleware applications built on top of Mezzio microframework and using Laminas components  Dotkernel Admin is a basic admin panel, based on Boostrap ^4.", "tl_dr": "Dotkernel Admin version 3 has launched as a basic admin panel built on Bootstrap ^4.5.0 and Doctrine, performing CRUD operations over a database on top of the Mezzio microframework and Laminas components.\nThe source is available on GitHub, with a live demo running at admin.dotkernel.net.\nBranch 3.0 is now the default branch, requiring Mezzio ^3.2, PHP ^7.4, Doctrine 2.7.x, Twig 3.x, and Bootstrap 4.5, with dot-* packages limited to version 3.x and above." }, @@ -1435,6 +1550,7 @@ "github": "sergiu@rospace.com" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Getting Started with Dotkernel Admin V4 Dotkernel's PSR-7 Admin is an application based on Mezzio, with the main purpose of managing and displaying tabular data from one or more databases components. On 19 July 2022 Dotkernel Admin V4 has been officially released.", "tl_dr": "Dotkernel Admin V4, officially released on 19 July 2022, is Dotkernel's PSR-7 Admin application built on Mezzio for managing and displaying tabular data from one or more databases.\nIt supports PHP 8.1 (minimum PHP 7.4), offers a config-driven module\/middleware\/route setup, RBAC-based authorization guards, a Symfony Console-based CLI with a file locker, per-module routing via RoutesDelegator, and a Bootstrap 4.5.0 \/ Fontawesome 5.0.6 frontend using Bootstrap Table for data listing." }, @@ -1447,6 +1563,7 @@ "github": "sergiu@rospace.com" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article refers to Dotkernel API v5. Checkout out the new additions for Dotkernel API v6 to stay up-to-date.", "tl_dr": "Dotkernel API is built on the Mezzio microframework and Laminas components, based on Enrico Zimuel's Zend Expressive API skeleton and implementing PSR-3, PSR-4, PSR-7, PSR-11, and PSR-15.\nIts core components include Doctrine ORM for persistence, mezzio-hal for API payloads, mezzio-cors for CORS handling, and mezzio-authentication-oauth2 for OAuth 2.0 authentication, alongside Postman-based documentation, configurable routing and commands, a file locker system, and a factory-made test suite." }, @@ -1459,6 +1576,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "With the release of PHP 8.3, the Dotkernel team has been working on updating the dependencies in our packages.", "tl_dr": "Dotkernel Admin added PHP 8.3 support in release 4.3.1, dropping PHP 8.1 and now supporting only PHP 8.2 and PHP 8.3.\nThe update brought numerous dependency bumps across dotkernel\/, laminas\/, and mezzio\/* packages, removed PhpFileCache-related cache configuration because doctrine\/cache dropped its implementation classes, and removed doctrine\/doctrine-module due to a conflict, which may affect packages that depended on it.\nThe AdminService::logAdminVisit method was also updated to no longer return AddressNotFoundException." }, @@ -1471,6 +1589,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "To be able to take advantage of the support for PHP 8.3 in the newest packages, the Dotkernel team has updated the Frontend Application to version 4.", "tl_dr": "To take advantage of PHP 8.3 support in the newest packages, the Dotkernel team updated the Frontend application to version 4.2.0.\nAs with the earlier Admin update, this required dropping support for PHP 8.1 and for the no-longer-available PhpFileCache class, until a replacement is implemented." }, @@ -1483,6 +1602,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/04\/PHP-8.3-support-in-Dotkernel-API.png", "excerpt": "The last remaining application to be updated to support PHP 8.3 is the API, now at v4.", "tl_dr": "Dotkernel API, now at v4.2.1, is the last remaining Dotkernel application updated to support PHP 8.3, following the same approach used for the Frontend update.\nThe update drops PHP 8.1 support, updates a large set of dependencies, removes the PhpFileCache-based configuration in favor of the new dot-cache package, and requires a small query change (useQueryCache() to setCacheable())." } @@ -1502,6 +1622,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/02\/twitter-card-dynamic-routing.png", "excerpt": "The goal of this update is to replace the static way of creating routes with a more dynamic implementation. The result is a cleaner approach that is easier to set up and review at a glance.", "tl_dr": "This article, the first in a series about switching from controllers to PSR-15 compliant handlers, explains how Dotkernel replaced its static, hard-coded route declarations with a centralized, dynamic configuration in local.php.\nThe change is aimed at static pages only - any method other than GET (post, put, delete) returns a 405 status code." }, @@ -1514,6 +1635,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/03\/twitter-card-controllers-to-handlers.png", "excerpt": "The goal of this update is to implement PSR-15 handlers into Dotkernel Light. There are several advantages to using handlers, which we will explore below.", "tl_dr": "The goal of this update is to implement PSR-15 handlers into Dotkernel Light, keeping the application up-to-date with recommended design guidelines, secure, and aligned with standards widely adopted by the PHP community." }, @@ -1526,6 +1648,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/05\/twitter-card-light-improvements.png", "excerpt": "Dotkernel Light is a PSR-15 compliant application that uses the Mezzio microframework and Laminas components. It's aimed at creating a simple website, like a presentation site, but can be expanded as needed.", "tl_dr": "Dotkernel Light is a PSR-15 compliant application built on Mezzio and Laminas, aimed at simple websites like presentation sites.\nSince its last update, it has moved from controllers to PSR-15 handlers, adopted Vite as its bundler, replaced Psalm with PHPStan, and picked up several smaller improvements." } @@ -1545,6 +1668,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Zend_Db and its related classes provide a simple SQL database interface for Zend Framework. To connect to MySql database, we are using Pdo_Mysql adapter : $db = Zend_Db::factory('Pdo_Mysql', $dbConnect); SELECT query - WHERE clause The below 2 classical SQL queries are equivalent.", "tl_dr": "Zend_Db and its related classes provide a simple SQL database interface for Zend Framework.\nThis 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." }, @@ -1557,6 +1681,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Continuing the Zend_DB article series, we are stopping now at FETCH methods that are in Zend_Db_Adapter_Abstract: array fetchAll (string|Zend_Db_Select $sql, , ) array fetchAssoc (string|Zend_Db_Select $sql, ) array fetchCol (string|Zend_Db_Select $sql, ) string fetchOne (string|Zend_Db_Select $sql, ) array fetchPairs (string|Zend_Db_Select $sql, ) array fetchRow (string|Zend_Db_Select $sql, , ) To 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.", "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.\nEach 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." }, @@ -1569,6 +1694,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Continuing the Zend_DB article series, 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: SELECT a.", "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." }, @@ -1581,6 +1707,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Continuing the Zend_DB article series, we are stopping now at DML statements. DML (Data Manipulation Language) statements are statements that change data values in database tables.", "tl_dr": "DML (Data Manipulation Language) statements change data values in database tables.\nThis 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." }, @@ -1593,6 +1720,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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 or with MySQL function NOW() ALTER TABLE `user` CHANGE `dateCreated` `dateCreated` TIMESTAMP NOT DEFAULT CURRENT_TIMESTAMP; CURRENT_TIMESTAMP is also a solution for  updating date and time fields.", "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.\nThis 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.\nOnly one TIMESTAMP field per table can be DEFAULT CURRENT_TIMESTAMP." }, @@ -1605,6 +1733,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Continuing the Zend_Db article series, let's discuss the LIKE condition. The LIKE condition allows you to use wildcards in the WHERE clause of an SQL statement.", "tl_dr": "The LIKE condition allows pattern matching in the WHERE clause of SELECT, INSERT, UPDATE, or DELETE statements.\nThe _ wildcard matches a single character, and % matches any string of any length (including zero).\nThis article shows how to use LIKE and NOT LIKE with both wildcards in Zend_Db." }, @@ -1617,6 +1746,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "To always redirect users to the www site (for example: http:\/\/dotboost.com to http:\/\/www.", "tl_dr": "" }, @@ -1629,6 +1759,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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.", "tl_dr": "svn export lets you export the contents of a repository into a virtual host directory.\nThe 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." }, @@ -1641,6 +1772,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "In Aptana it's very simple to set the svn:keywords property for a file. 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$ Right click on the file, then follow Team -> Set Property.", "tl_dr": "" }, @@ -1653,6 +1785,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "1.  Always use in development and in staging highest error reporting level, and display_errors ON: error_reporting(-1); ini_set('display_errors', 1); 2.", "tl_dr": "" }, @@ -1665,6 +1798,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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 SVN Ignore files, below you have an example.", "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.\nThis article lists which properties to set and how." }, @@ -1677,6 +1811,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/07\/twitter-card-retired-whats-next-1.png", "excerpt": "It all started with the announcement: Laminas MVC Is Retiring. Some people wrongfully thought everything with a Laminas logo is going away - NOT SO! Read on for a bit of history about Zend and Laminas, what it means to migrate your platform and why it's a decision that should not be taken lightly.", "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.\nMaintaining 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." }, @@ -1689,6 +1824,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/10\/twitter-card-basic-security.png", "excerpt": "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.", "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." } @@ -1708,6 +1844,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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 leaving the others with nothing to receive. This can be the case where you use 3rd party libraries with broadcast receivers defined.", "tl_dr": "" }, @@ -1720,6 +1857,7 @@ "github": "" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Have you ever wondered if Android market sends you information at the moment of app install? Wouldn\"t 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 online casino 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.", "tl_dr": "" } @@ -1739,6 +1877,7 @@ "github": "arhimede" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article covers the basic authorization of a Client application which use a backend built using Dotkernel API Authorization Request Client application users send a POST request to the backend containing the following JSON object: { \"grant_type\": \"password\", \"client_id\": \"{API_CLIENT}\", \"client_secret\": \"{API_CLIENT_SECRET}\", \"scope\": \"{SCOPE}\", \"username\": \"{USERNAME\/EMAIL}\", \"password\": \"{PASSWORD}\" } Authorization Response If the credentials are correct, the API will return a JSON object containing the authentication data: { \"token_type\": \"Bearer\", \"expires_in\": 86400, \"access_token\": \"..", "tl_dr": "" }, @@ -1751,6 +1890,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article covers the basic authorization of a Server Side application  built using Dotkernel API Protecting an endpoint no-auth: the resource can be accessed without the need of authentication\/authorization authentication: the resource can be accessed only by authenticated users authorization: the resource can be accessed only by authenticated AND authorized users Configuring access to the endpoints is done by editing the following config file: config\/autoload\/authorization.local.", "tl_dr": "Dotkernel API endpoints can be protected at three levels: no-auth, authentication, and authorization.\nAccess 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.\nAuthentication 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." }, @@ -1763,6 +1903,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "This article will walk you through the process of implementing MailChimp into your instance of Dotkernel API using drewm\/mailchimp-api   Step 1: Add the library to your application using the following command: composer require drewm\/mailchimp-api   Step 2: Create configuration file config\/autoload\/mailchimp.global.", "tl_dr": "This is a step-by-step guide to adding MailChimp support to a Dotkernel API instance using the drewm\/mailchimp-api library.\nIt 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." }, @@ -1775,6 +1916,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "Dotkernel API has come a long way since this post was created. Check out the newest version of Dotkernel API to stay up to date with the latest functional and security features.", "tl_dr": "" }, @@ -1787,6 +1929,7 @@ "github": "sergiu@rospace.com" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "API Endpoint to Collect Client Errors Let's say you have a (Client) Frontend (e.g.", "tl_dr": "" }, @@ -1799,6 +1942,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "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.", "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.\nIt 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." }, @@ -1811,6 +1955,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/07\/OpenAPI-implementation-in-Dotkernel-API.png", "excerpt": "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.", "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.\nDotkernel 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.\nTesting protected endpoints in Swagger UI requires generating an authentication token that matches the endpoint's required privileges." }, @@ -1823,6 +1968,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/08\/error-reporting-endoint-in-dotkernel-api.png", "excerpt": "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.", "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.\nIt 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.\nSetup 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." }, @@ -1835,6 +1981,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/11\/twitter-card-content-negotiation.png", "excerpt": "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.", "tl_dr": "Content negotiation lets clients and servers agree on the format and language of exchanged data.\nIt 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." }, @@ -1847,6 +1994,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2026\/03\/twitter-card-postman-to-bruno.png", "excerpt": "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.", "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.\nBruno 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." } @@ -1866,6 +2014,7 @@ "github": "alexmerlin" }, "isObsolete": false, + "opengraph_img": null, "excerpt": "PHP_CodeSniffer or phpcs is a tool that helps developers maintain a specific standard in the way they write code. In order to be able to provide relevant information, phpcs needs to be configured correctly in PHPStorm (see image).", "tl_dr": "PHP_CodeSniffer (phpcs) needs to be configured correctly in PHPStorm under PHP > Quality Tools > PHP_CodeSniffer, with the Custom coding standard pointed at your project's phpcs.xml file.\nThis article gives separate setup steps for a freshly cloned project versus an existing one that isn't reporting issues yet, and explains how to read the resulting inline error and warning indicators in the editor." } @@ -1885,6 +2034,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2024\/04\/MIT-VERSUS-LGPL-IN-PRACTICE-1.png", "excerpt": "After a recent analysis, we discovered that one of the upstream packages we use is licensed under LGPL v3. Even though we at Dotkernel use the MIT license for our open source projects, the more restrictive license must be applied to the whole application.", "tl_dr": "Dotkernel discovered that an upstream dependency, matomo\/device-detector, was licensed under LGPL v3 - a more restrictive license than the MIT license Dotkernel uses for its own projects.\nBecause the more restrictive license would have to apply to the whole application, Dotkernel implemented a workaround: it stopped bundling that dependency by default and documented the licensing implications." } @@ -1904,6 +2054,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/05\/twitter-card-understanding-middleware.png", "excerpt": "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 Middleware makes it easier for software developers to implement communication and input\/output, so they can focus on the specific purpose of their application.", "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.\nIt'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." }, @@ -1916,6 +2067,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/08\/twitter-card-config-provider.png", "excerpt": "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.", "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.\nFrameworks 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)." }, @@ -1928,6 +2080,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2026\/05\/twitter-card-request-lifecycle.png", "excerpt": "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. The graph below shows how the request is handled by Dotkernel Light (GitHub, documentation), one of the applications in the Dotkernel Headless Platform suite.", "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.\nThis 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." } @@ -1947,6 +2100,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/04\/twitter-card-naming-patten.png", "excerpt": "This naming pattern is used in Dotkernel Admin v6 and will also be implemented in the next releases for Frontend and Light. The bigger a project is, the more time it will take to develop and the more people will be assigned to it.", "tl_dr": "" } @@ -1966,6 +2120,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/06\/twitter-card-headless-platform1.png", "excerpt": "The principle of a Headless Platform is to decouple the User Interface (frontend) from the backend services. The responses from the platform are then used by another system, such as a website or mobile app.", "tl_dr": "A Headless Platform decouples the frontend (UI) from the backend services, with responses consumed by another system such as a website or mobile app. The Dotkernel Headless Platform is made up of Dotkernel API (a REST API based on the Mezzio skeleton) and Dotkernel Admin (a backend management interface), which can be installed separately or together.\nUsing both together, sharing a common Core module, gives consistent entities and queries, an easy-to-maintain shared file structure, and an architecture that scales from small microservices to enterprise-grade APIs." }, @@ -1978,6 +2133,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/07\/twitter-card-core-submodule.png", "excerpt": "Dotkernel has implemented a Headless solution made up of these applications: Dotkernel API - a REST API, the root of the platform. Dotkernel Admin - (optional) complementary backend management.", "tl_dr": "Dotkernel's Headless Platform is composed of Dotkernel API, Admin, and Queue, and can share a common Core submodule that holds the database entities and services used consistently across all of them.\nThe article walks through creating the Core submodule with git submodule add, committing changes from within the Core folder, and initializing\/updating it with git submodule init and git submodule update.\nSharing a Core module brings design flexibility, scalability, and easier bugfixes and onboarding as the platform grows." }, @@ -1990,6 +2146,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/07\/twitter-card-api-v6.png", "excerpt": "Dotkernel API has come a long way since we published a list of its architecture and components a while ago. We implemented new features, while some components were replaced, and others were enhanced.", "tl_dr": "Dotkernel API has evolved significantly since its original architecture and components article, adding Content Negotiation, standardized error responses via mezzio-problem-details, a shareable Core module, a custom templating solution replacing Twig, and a leaner handler dependency setup.\nPackages were updated across the board, the test suite switched from Psalm to PHPStan at a stricter rule level, and the roadmap for v6.1 targets Service Manager 4 and PHP 8.4\/8.5 support." }, @@ -2002,6 +2159,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/08\/twitter-card-complementary-admin.png", "excerpt": "The Dotkernel Headless Platform is built with an architecture designed to be easy to maintain and expand indefinitely. Its core components are Dotkernel API and Dotkernel Queue, but the Dotkernel application suite also offers a fully separate, complementary Admin application designed to pair seamlessly with Dotkernel API.", "tl_dr": "The Dotkernel Headless Platform's core components are Dotkernel API and Dotkernel Queue, but the suite also offers a fully separate, complementary Admin application designed to pair seamlessly with Dotkernel API.\nAdmin is an independent app built on the same Mezzio + Laminas foundation, sharing a unified tech stack with the API so the two form a cohesive, consistent system." }, @@ -2014,6 +2172,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/09\/twitter-card-queue.png", "excerpt": "Dotkernel Queue is a component based on Symfony Messenger that is used to queue asynchronous tasks. netglue\/laminas-messenger is an adapter that integrates Symfony Messenger with the Laminas Service Manager container for Mezzio\/Laminas applications.", "tl_dr": "Dotkernel Queue is a component built on Symfony Messenger (via the netglue\/laminas-messenger adapter) that lets time-consuming or resource-intensive operations run asynchronously on background workers instead of inside the normal PHP request-response cycle.\nAn active daemon listens for TCP connections, stores incoming messages in Redis, and processes them in FIFO order, with logging, IP-whitelisting security, a configurable retry mechanism, reporting metrics, and a Dead Letter Queue for messages that fail.\nPriorities and parallel execution are planned future features." }, @@ -2026,6 +2185,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/09\/twitter-card-dotmaker-2.png", "excerpt": "The dotkernel\/dot-maker library, also named DotMaker, is designed to programmatically generate project files and directories that match the Dotkernel file structure inspired by Mezzio. Handling the file creation and configuration task manually invites mistakes that nobody has time for.", "tl_dr": "DotMaker (dotkernel\/dot-maker) programmatically generates project files and directories matching the Dotkernel file structure inspired by Mezzio.\nIt boosts productivity and enforces consistency and standardization compared to creating modules and files by hand, and it can tell the difference between Dotkernel applications (Api, Admin, Frontend) to create the files each one requires." }, @@ -2038,6 +2198,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/12\/twitter-card-api-evolution-versioning.png", "excerpt": "In programming and software architecture, an Evolution Pattern is a reusable, high-level strategy for modifying or evolving existing software systems over time. An evolution pattern tries to keep software relevant for old and new users by whatever means are available, as new needs arise.", "tl_dr": "An Evolution Pattern keeps the same codebase and evolves it gradually (for example via sunsetting), while API versioning maintains multiple parallel versions of an API so existing clients aren't broken.\nThe two are not mutually exclusive.\nDotkernel API favors an evolution pattern with a sunsetting mechanism, reserving full versioning for major, format-level changes." }, @@ -2050,6 +2211,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2025\/12\/twitter-card-headless-v7.png", "excerpt": "The Dotkernel Headless Platform has seen new releases for both API and Admin. The Admin codebase has received an overall facelift, as well as updates to retain compatibility with API v7.", "tl_dr": "Dotkernel API v7 adds support for native UUID v7, PostgreSQL, PHP 8.5 (8.4 for Admin), database table prefixes, and improved database configuration, while replacing the binary data type for id columns with uuid.\nIt drops the Evolution pattern's Method Deprecation support and MySQL, since MySQL doesn't support the UUID data type.\nUUIDs are generated with the ramsey\/uuid package, previously uuid-named table columns are now called id, and PostgreSQL or MariaDB v10.7+ is required for UUID support." }, @@ -2062,6 +2224,7 @@ "github": "bidi47" }, "isObsolete": false, + "opengraph_img": "https:\/\/www.dotkernel.com\/wp-content\/uploads\/2026\/04\/twitter-card-totp.png", "excerpt": "What TOTP Does A Time-based One-Time Password (TOTP) is a security algorithm used as part of two-factor authentication (2FA) to protect against account attacks. The mechanism is integrated into dot-totp to enhance security by requiring both a password and an additional one-time code.", "tl_dr": "dot-totp adds two-factor authentication (2FA) to Dotkernel Admin using time-based one-time passwords.\nUsers authenticate with their password plus a 6-digit code from an Authenticator app that refreshes every 30 seconds.\nInstallation is one Composer command plus a set of forms, handlers, middleware, and templates from the official code examples, applying a TotpTrait to the relevant entity, migrating three new database columns, and registering routes\/pipeline\/ConfigProvider updates." } diff --git a/src/App/src/Migration/Version20260803153306.php b/src/App/src/Migration/Version20260803153306.php new file mode 100644 index 0000000..c9f4cf8 --- /dev/null +++ b/src/App/src/Migration/Version20260803153306.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE post ADD opengraph_img LONGTEXT DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE post DROP opengraph_img'); + } +} diff --git a/src/App/src/Migration/Version20260803200417.php b/src/App/src/Migration/Version20260803200417.php new file mode 100644 index 0000000..535eaf0 --- /dev/null +++ b/src/App/src/Migration/Version20260803200417.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE post CHANGE opengraph_img opengraph_img VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE post CHANGE opengraph_img opengraph_img LONGTEXT DEFAULT NULL'); + } +} diff --git a/src/App/src/Service/FeedGenerator.php b/src/App/src/Service/FeedGenerator.php index fc186b8..37ad195 100644 --- a/src/App/src/Service/FeedGenerator.php +++ b/src/App/src/Service/FeedGenerator.php @@ -73,9 +73,10 @@ public function write(): int ); $this->appendText($dom, $item, 'guid', $link); - if ($this->image !== '') { + $image = $post->getOpenGraphImage() ?? $this->image; + if ($image !== '') { $media = $dom->createElementNS(self::MEDIA_NAMESPACE, 'media:content'); - $media->setAttribute('url', $this->image); + $media->setAttribute('url', $image); $media->setAttribute('medium', 'image'); $item->appendChild($media); } diff --git a/src/App/templates/partial/meta.html.twig b/src/App/templates/partial/meta.html.twig index c8b3971..e8214ee 100644 --- a/src/App/templates/partial/meta.html.twig +++ b/src/App/templates/partial/meta.html.twig @@ -3,16 +3,16 @@ {{ meta.title ?? meta.name ?? app.meta.title }} - + - - + + - + - + diff --git a/src/Blog/src/Entity/Post.php b/src/Blog/src/Entity/Post.php index 281a624..3a62be2 100644 --- a/src/Blog/src/Entity/Post.php +++ b/src/Blog/src/Entity/Post.php @@ -49,6 +49,9 @@ enumType: PostStatusEnum::class, #[ORM\Column(name: 'isObsolete', type: 'boolean')] private bool $isObsolete = false; + #[ORM\Column(name: 'opengraph_img', type: 'string', length: 255, nullable: true)] + private ?string $openGraphImage = null; + public function getTitle(): string { return $this->title; @@ -139,6 +142,16 @@ public function setObsolete(bool $isObsolete): void $this->isObsolete = $isObsolete; } + public function getOpenGraphImage(): ?string + { + return $this->openGraphImage; + } + + public function setOpenGraphImage(?string $openGraphImage): void + { + $this->openGraphImage = $openGraphImage; + } + /** * @return array{ * id: non-empty-string, @@ -149,6 +162,7 @@ public function setObsolete(bool $isObsolete): void * tlDr: string|null, * postDate: string, * isObsolete: bool, + * openGraphImage: string|null, * category: array{id: non-empty-string, name: string, slug: string}, * author: array{id: non-empty-string, name: string, slug: string, github: string|null} * } @@ -156,16 +170,17 @@ public function setObsolete(bool $isObsolete): void public function getArrayCopy(): array { return [ - 'id' => $this->id->toString(), - 'title' => $this->title, - 'slug' => $this->slug, - 'status' => $this->status->value, - 'excerpt' => $this->excerpt, - 'tlDr' => $this->tlDr, - 'isObsolete' => $this->isObsolete, - 'postDate' => $this->postDate->format('Y-m-d H:i:s'), - 'category' => $this->category->getArrayCopy(), - 'author' => $this->author->getArrayCopy(), + 'id' => $this->id->toString(), + 'title' => $this->title, + 'slug' => $this->slug, + 'status' => $this->status->value, + 'excerpt' => $this->excerpt, + 'tlDr' => $this->tlDr, + 'isObsolete' => $this->isObsolete, + 'openGraphImage' => $this->openGraphImage, + 'postDate' => $this->postDate->format('Y-m-d H:i:s'), + 'category' => $this->category->getArrayCopy(), + 'author' => $this->author->getArrayCopy(), ]; } } From 76e192034d89d59a6a9693213ff465c80ab528d2 Mon Sep 17 00:00:00 2001 From: OStefan2001 Date: Tue, 4 Aug 2026 10:02:29 +0300 Subject: [PATCH 2/3] Fix path script to use relative paths --- bin/update-path-opengraph-images | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/update-path-opengraph-images b/bin/update-path-opengraph-images index 0063377..1ef622f 100755 --- a/bin/update-path-opengraph-images +++ b/bin/update-path-opengraph-images @@ -96,7 +96,7 @@ foreach ($posts as $post) { continue; } - $post->setOpenGraphImage($baseUrl . '/uploads/opengraph/article/' . $post->getId()->toString() . '/' . $filename); + $post->setOpenGraphImage('/' . $targetPath); $postsUpdated++; } From fe23fa9ee8bd91f2c6ba306fda05ac643965ce2c Mon Sep 17 00:00:00 2001 From: OStefan2001 Date: Tue, 4 Aug 2026 10:57:15 +0300 Subject: [PATCH 3/3] Update where is used --- src/App/src/Service/FeedGenerator.php | 4 +++- src/App/templates/partial/meta.html.twig | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/App/src/Service/FeedGenerator.php b/src/App/src/Service/FeedGenerator.php index 37ad195..7b2b7be 100644 --- a/src/App/src/Service/FeedGenerator.php +++ b/src/App/src/Service/FeedGenerator.php @@ -73,7 +73,9 @@ public function write(): int ); $this->appendText($dom, $item, 'guid', $link); - $image = $post->getOpenGraphImage() ?? $this->image; + $image = $post->getOpenGraphImage(); + $image = $image !== null && $image !== '' ? $this->baseUrl . $image : $this->image; + if ($image !== '') { $media = $dom->createElementNS(self::MEDIA_NAMESPACE, 'media:content'); $media->setAttribute('url', $image); diff --git a/src/App/templates/partial/meta.html.twig b/src/App/templates/partial/meta.html.twig index e8214ee..c196c3c 100644 --- a/src/App/templates/partial/meta.html.twig +++ b/src/App/templates/partial/meta.html.twig @@ -6,7 +6,7 @@ - +