Blogs / 7x / 7x Releases Exponential Basic 2.4.0.2

7x Releases Exponential Basic 2.4.0.2

Monday 09 March 2026 5:17:36 am

  • Currently 3 out of 5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

By : Graham Brookins

7x is very proud of the work released today as Exponential Basic 2.4.0.2 which is a major upgrade to the core kernel of Exponential 2.4 series.

This upgrade provides a lot more features under the hood while on the surface the same simple to use cms is retained.

Download the 2.4.0.2 release and give it a try, it's well documented with simple to follow instructions for getting started.

Have a question? Create a forum post and ask the community for help answering your questions with answers.

Changes from 2.4.0.1 to 2.4.0.2
---------------------------
PERFORMANCE — eztrade productlist N+1 query elimination
- Added: eZProduct::prewarmThumbnails( array $ids ) batch-loads all ProductImageDefinition
  rows for a product listing in a single IN() query, then calls eZImage::prefetch() to load
  all required image rows in a second query. Previously each product triggered two individual
  SELECT statements (one for the thumbnail definition, one for the image record), resulting
  in up to 2N database queries per page. After this change the same work costs 2 queries
  regardless of how many products are on the page.
- Added: eZImage::prefetch( array $ids ) static batch-loader that populates eZImage::$rowCache.
  Individual eZImage constructor calls consult this cache first and skip their SELECT if the
  row is already present.
- Added: eZImageVariation::prefetchByGroup( $groupID, array $imageIDs, $modification ) batch-loads
  all image variation rows for a set of image IDs and one variation group in a single IN() query.
  Results are stored in eZImageVariation::$cache keyed by "groupID|imageID|modification". Cache
  entries for IDs with no matching variation are recorded as null (confirmed-absent) so
  getByGroupAndImage() never issues a redundant SELECT for the same combination.
- Added: eZImageVariationGroup::$getCache and eZImageVariationGroup::$existsCache static caches
  so variation group lookups (groupExists, get) are resolved from memory within the same request.
- Added: eZProduct::hydrateRow( $row, $db ) hydrates a product object from an already-fetched
  DB row array. Used by the updated eZProduct::products() / eZProductCategory::activeProducts()
  bulk-load path to avoid N individual eZTrade_Product SELECT calls per listing page.
- Updated: eZProduct::products() (called internally by eZProductCategory::activeProducts())
  batch-loads all required product rows with a single IN() query against eZTrade_Product and
  hydrates each eZProduct object via hydrateRow(), replacing the previous pattern of constructing
  each eZProduct with an individual SELECT.
- Added: eZProduct::$optionsCache (instance), eZProduct::$vatTypeCache (instance),
  eZProduct::$correctPriceCache (instance, keyed by calcVAT|withPriceGroups),
  eZProduct::$briefCache (instance), eZProduct::$thumbnailIDCache (static) — instance-level
  and request-level caches on compute-heavy methods. Methods such as vatType(), correctPrice(),
  options(), brief(), and thumbnailImage() are each called multiple times per product per request
  (from price-range calculation, template rendering, and option loops). These caches make each
  method effectively free after the first call per object.
- Updated: eZProduct::hasOptions() simplified to delegate to options(), which is now cached.
  The previous implementation issued its own separate COUNT query.
- Added: eZVATType::get() static cache keyed by VAT type ID; eZPriceGroup::correctPriceGroup()
  static cache keyed by user ID + mode; eZProductCurrency::getAll() result hoisted out of the
  product-loop in productlist.php (was re-fetched on every iteration).
- Added: eZProductCategory::$getCache static cache so get() calls for the same category ID
  within one request skip their SELECT query.
- Added: productlist.php performance flags via site.ini [eZTradeMain]:
    ShowProductOptions=enabled|disabled  — disabling skips the per-product option/variant loop
    which previously cost ~30 queries per product (5 options × ~6 queries each).
    ShowThumbnailImages=enabled|disabled — disabling skips the thumbnail fetch/resize path.
  Both flags default to enabled so existing sites are unaffected with no configuration change.
- Updated: productlist.php hoists eZObjectPermission::hasPermission() outside the product loop.
  The permission check used identical arguments on every iteration; it is now evaluated once
  before the loop and the cached boolean is reused.
PERFORMANCE — eztrade productlist page-level cache
- Added: Page-level output cache for the productlist view, matching the existing pattern used
  by productgallery, hotdealsgallery, and hotdealslist views. On the first request the full
  rendered HTML is written to kernel/eztrade/cache/ via eZCacheFile::store(). Subsequent
  requests for the same URL (same category ID, user group array, offset, and price group)
  serve the cache file directly via include(), bypassing all database queries and template
  rendering entirely.
- Updated: kernel/eztrade/user/datasupplier.php productlist case unconditionally sets
  $generateStaticPage = true (matching the productgallery pattern) and reads the cache file
  via eZCacheFile::exists() + include( $cacheFile->filename(true) ) when a valid cached copy
  is present. Cache-key components: ("productlist", $categoryID, $groupIDArray, $offset, $priceGroup).
- Updated: settings/site.ini [eZTradeMain] PageCaching changed from disabled to enabled so
  the page cache is active for all eztrade views including the newly cached productlist.
BUG FIXES — eztrade / ezimagecatalogue
- Fixed: eZProduct::name() — added null-coalescing fallback ('' ) to stripslashes() call to
  suppress PHP 8.x Deprecated/TypeError for products with a null Name field.
- Fixed: productview.php line 489 — Undefined array key warning when $headers is empty.
  SimpleOptionHeaders path now reads $headers[0] via isset check, returning '' when no header
  is present, preventing a spurious warning on products with option definitions that carry
  no description headers.
- Fixed: productview.php line 633 — "Only variables should be assigned by reference" notice.
  $value =& $attributes[$i]->value($product) cannot assign a reference from a method return
  value in PHP 8. Changed to $value = $attributes[$i]->value($product).
- Fixed: eZImageVariation — removed stale =& reference assignment on $this->ID and related
  fields (PHP 8 notice); changed to plain assignment.
- Fixed: eZImage::store() — $originalfilename was incorrectly bound using the DB field-name
  lookup instead of escapeString. Changed to $db->escapeString($this->OriginalFileName).
- Fixed: eZImage — array_pop( explode(...) ) passed a literal expression to array_pop(), which
  requires a variable (PHP 8 strict notice). Intermediate variable $tmpNameParts introduced.
BUG FIXES — SQLite driver (ezsqlite3db)
- Fixed: eZSQLite3DB::query( 'PRAGMA journal_mode = wal;' ) — previously the failure path
  printed an HTML error and called exit(), which terminated responses mid-stream when a
  concurrent writer held the database lock. File downloads triggered immediately after a write
  would produce a 0-byte or truncated response. The call is now non-fatal: WAL mode is
  requested and silently skipped when it cannot be set; the database continues in rollback-
  journal mode.
- Added: eZSQLite3DB — SQLite3::busyTimeout(5000) is now set directly on the connection object
  before any PRAGMA statements, so lock-retry behaviour applies from the very first query.
- Added: eZSQLite3DB — nested transaction guard via $this->TransactionDepth counter. Calling
  beginTransaction() when a transaction is already open no longer issues a second BEGIN
  TRANSACTION (invalid in SQLite), and a matching commit() only issues COMMIT when the depth
  reaches zero. This prevents "cannot start a transaction within a transaction" errors under
  code paths that call begin/commit pairs at more than one stack level.
- Updated: eZSQLite3DB — $Type property declaration moved to class body (PHP 8.2 dynamic
  property deprecation). $TransactionDepth declared as var for PHP 4-compatible class body
  to avoid declaring typed properties.
BUG FIXES — schema (update/database/schema/mysql/)
- Added: fix_ezlink_hit_schema — drops and re-creates eZLink_Hit table which was originally
  created with the column layout of eZLink_Link instead of the correct hit-tracking schema
  (ID, Link FK, Time, RemoteIP). The table contains only transient click-tracking data; no
  persistent data is lost. Migration scripts provided for MySQL, PostgreSQL, and SQLite.
- Added: add_article_section_dict_sectionid — adds the SectionID column to
  eZArticle_ArticleSectionDict, required by the section-linking query in
  kernel/classes/ezmodulelink.php. Migration scripts provided for MySQL, PostgreSQL, and SQLite.
BUG FIXES — kernel / framework
- Fixed: ezpbkernelweb.php — $GlobalSiteIni =& $ini reference assignment replaced with plain
  assignment ($GlobalSiteIni = $ini) to suppress PHP 8.x deprecation. The SiteDir, WWWDir,
  and Index properties are set on the object afterward; no behaviour change.
- Fixed: ezpbkernelweb.php / ezpbkerneladmin.php — $moduleResult ?? null guard added in two
  locations where $moduleResult could be unset before being passed to ezpbKernelResult and
  template rendering, preventing Undefined variable notices.
- Fixed: ezpbkernelweb.php — gotolink requests (redirect-only) now exit immediately after the
  datasupplier include so no HTML layout is rendered around the redirect response.
CODE QUALITY — global variable naming convention
- Updated: datasupplier.php (eztrade user) and all included view files (productlist.php,
  productview.php, productgallery.php, hotdealsgallery.php, hotdealslist.php, cart.php,
  checkout.php, and approximately 60 additional view files across all kernel modules) —
  HTTP request variables previously accessed as PascalCase globals ($CategoryID, $ProductID,
  $Offset, $PriceGroup, $ModuleName, $CapitalizeHeadlines, $AdminSiteURL, $UserReviews, etc.)
  renamed to camelCase locals ($categoryID, $productID, $offset, $priceGroup, $moduleName,
  $capitalizeHeadlines, $adminSiteURL, $userReviews, etc.) to align with the project
  convention established in 2.4.0.2 for eZHTTPTool::getVar() introduced variables. All
  datasupplier files centralise the eZHTTPTool::getVar() calls at the top of the file so
  downstream view includes receive named locals rather than relying on register_globals-era
  PascalCase names.
- Updated: kernel/classes/linklist.php (admin) — minor PHP 8 compatibility cleanup.
- Updated: Added example store site design, 'ecommerce' to convert from short php tags to full php tags within design templates in php.
- Updated: Refactored entire source code to convert from short php tags to full php tags within all php files.
- Updated: Refactored entire source code to remove all unsupported references usage within all php files.
- Updated: Refactored entire source code to remove add php 8.4 / 8.5.3 (tested) within all php files.
- Updated: Refactored entire source code to replace a tested iteration of all module views using eZHTTPTool::getVar instead of the register globals support (now removed).
- Updated: Refactored entire source code to add performance improvements which reduce boot speed 95% overall by fixing all notices, warnings, errors and deprecations within all php files.

Be sure to review the basic.exponential.earth open source project website for more information and future releases!