OpenCart on OxPHP

OpenCart is a traditional-mode application with two physical front controllers: index.php (storefront) and admin/index.php (admin panel), plus static assets under image/ and catalog/view/. Its webroot is the project root itself. OxPHP's traditional routing mode, the default when there is no ENTRY_FILE, serves both entry points as real files and static assets from disk. PHP_DENY_PATHS blocks direct execution of the framework internals.

Stack at a glance

  • OxPHP image: ghcr.io/oxphp/oxphp:0.13.0-php8.4-alpine3.23 (PHP 8.4)
  • Routing mode: Traditional (no ENTRY_FILE; DOCUMENT_ROOT = project root, overrides the default …/public)
  • Extensions added: gd, mysqli, zip, mbstring
  • Services: OxPHP + MySQL
  • URL: http://localhost:8095 · admin /admin/ · internal http://localhost:9096/health
PHP version

OpenCart 4 requires PHP 8.0+ but its codebase predates 8.5; pin the PHP 8.4 image. OpenCart uses the mysqli driver, not PDO. (OpenCart 3.x will not run on PHP 8.4+, so use OpenCart 4.x.)

Project layout

OpenCart ships as a release archive, not a Composer package. The webroot is the contents of the archive's upload/ directory:

bash
mkdir -p opencart-oxphp/src curl -sL https://github.com/opencart/opencart/releases/download/4.1.0.3/opencart-4.1.0.3.zip -o /tmp/oc.zip unzip -q /tmp/oc.zip -d /tmp/oc cp -a /tmp/oc/upload/. opencart-oxphp/src/ # the installer needs these to exist and be writable: : > opencart-oxphp/src/config.php : > opencart-oxphp/src/admin/config.php

Dockerfile and Compose

The Dockerfile is src/Dockerfile.oxphp, named so it won't collide with OpenCart's bundled Dockerfile.

src/Dockerfile.oxphp
ARG OXPHP_VERSION=0.13.0 ARG PHP_VERSION=8.4 ARG ALPINE_VERSION=3.23 # ── PHP base: OpenCart runtime extensions ───────────────────── FROM php:${PHP_VERSION}-zts-alpine${ALPINE_VERSION} AS php-base RUN apk add --no-cache \ libpng libjpeg-turbo freetype oniguruma libzip \ && apk add --no-cache --virtual .build-deps \ libpng-dev libjpeg-turbo-dev freetype-dev oniguruma-dev libzip-dev \ && docker-php-ext-configure gd --with-jpeg --with-freetype \ && docker-php-ext-install -j"$(nproc)" gd mysqli zip mbstring \ && apk del .build-deps # ── Composer ────────────────────────────────────────────────── FROM composer:2 AS composer # ── OxPHP artifacts (PHP 8.4 image) ─────────────────────────── FROM ghcr.io/oxphp/oxphp:${OXPHP_VERSION}-php${PHP_VERSION}-alpine${ALPINE_VERSION} AS oxphp # ── dev: OxPHP server + PHP CLI + Composer ──────────────────── FROM php-base AS dev RUN apk add --no-cache libgcc COPY --from=composer /usr/bin/composer /usr/local/bin/composer COPY --from=oxphp /usr/local/bin/oxphp /usr/local/bin/oxphp COPY --from=oxphp /usr/local/lib/liboxphp_bridge.so /usr/local/lib/ COPY --from=oxphp /usr/local/lib/php/extensions/ /tmp/oxphp-ext/ RUN cp /tmp/oxphp-ext/*/oxphp_sapi.so "$(php -r 'echo ini_get("extension_dir");')/" \ && rm -rf /tmp/oxphp-ext \ && echo "extension=oxphp_sapi.so" > /usr/local/etc/php/conf.d/oxphp-ext.ini RUN { \ echo "[opcache]"; echo "opcache.enable=1"; echo "opcache.enable_cli=1"; \ echo "opcache.validate_timestamps=1"; echo "opcache.revalidate_freq=0"; \ } > /usr/local/etc/php/conf.d/opcache-dev.ini RUN { \ echo "memory_limit=256M"; echo "upload_max_filesize=64M"; \ echo "post_max_size=64M"; echo "max_execution_time=300"; \ } > /usr/local/etc/php/conf.d/opencart.ini RUN getent passwd www-data >/dev/null \ || adduser -D -H -u 82 -G www-data -s /sbin/nologin www-data RUN mkdir -p /var/www/html && chown -R www-data:www-data /var/www/html ENV LD_LIBRARY_PATH=/usr/local/lib WORKDIR /var/www/html EXPOSE 80 9090 CMD ["oxphp"]

Install and first run

OpenCart 4 ships a CLI installer:

bash
docker compose up -d db docker compose run --rm app php install/cli_install.php install \ --username admin --password 'admin123' --email admin@example.com \ --http_server 'http://localhost:8095/' --language en-gb \ --db_driver mysqli --db_hostname db --db_username opencart \ --db_password opencart --db_database opencart --db_port 3306 --db_prefix oc_ # OpenCart recommends removing the installer afterwards: rm -rf opencart-oxphp/src/install docker compose up -d app

OxPHP notes

Note
  • Traditional mode serves two front controllers. index.php (storefront) and admin/index.php (admin) are both physical entry points, and traditional mode runs each as a real file. Framework mode would funnel everything to one index.php and break the admin.
  • PHP_DENY_PATHS protects the internals. With the webroot at the project root, system/ and install/ sit inside DOCUMENT_ROOT. PHP_DENY_PATHS=/system/**,/install/** blocks direct HTTP execution of those scripts. The match happens before disk I/O, so there is no existence oracle. The CLI installer runs over the shell and is unaffected. See PHP Execution Deny-List.
  • Dotfiles are already safe. .env-style and dot-segment paths return 404 through dot-path blocking without any rule.
  • SEO URLs are off by default. Default OpenCart uses index.php?route=… query routing, which traditional mode handles directly with no rewrite rules.

Verify

bash
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8095/ # 200 storefront curl -s -o /dev/null -w "%{http_code}\n" "http://localhost:8095/index.php?route=common/home" # 200 curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8095/admin/ # 200 admin login curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8095/system/startup.php # 404 (denied) curl -s -o /dev/null -w "%{http_code}\n" http://localhost:9096/health # 200

Prompts for your agent

These prompts assume an agent with the OxPHP documentation MCP server connected. The agent reads the pages it needs itself.

prompt
Run my OpenCart store on OxPHP locally. Read the `examples/ecommerce/opencart` page through the OxPHP documentation MCP server. Write the Dockerfile and the Compose file for this tree — traditional mode, the project root as the document root, and deny-list globs that cover the internals my install actually has — then give me the CLI install command.
prompt
My OpenCart store on OxPHP serves the storefront, but the admin panel or my product URLs come back 404. Read the `examples/ecommerce/opencart` page through the OxPHP documentation MCP server. Check whether my Compose file sets `ENTRY_FILE`, what my `PHP_DENY_PATHS` globs cover, and whether SEO URLs are on in my store settings, then tell me which is eating the request.

See also

Found a mistake? Report it →