Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/php-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: PHP quality checks

on:
push:
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
quality:
name: PHP ${{ matrix.php-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-version: ['8.2', '8.3', '8.4', '8.5']

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: none
tools: parallel-lint, phpcs, phpstan

- name: Lint PHP sources
run: parallel-lint --exclude .git --exclude vendor src

- name: Check coding standards
run: phpcs --standard=phpcs.xml.dist

- name: Run static analysis
run: phpstan analyse --no-progress --configuration=phpstan.neon.dist
19 changes: 19 additions & 0 deletions docs/art-ipf-migration-matrix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Art → IPF migration matrix

This module still mixes legacy Art-based persistence patterns with newer ImpressCMS conventions. The matrix below captures the intended migration target without changing runtime behavior in this PR.

| Legacy area | Current responsibility | IPF-oriented target | Notes for follow-up |
| --- | --- | --- | --- |
| `src/class/art/object.php` and related Art base classes | Shared object lifecycle, validation, and persistence helpers | Replace with IPF object and handler base classes | Start with low-risk handlers that already map cleanly to one table each |
| `src/class/forum.php`, `src/class/topic.php`, `src/class/post.php` | Module domain objects and handler logic | Move table mapping and CRUD concerns into dedicated IPF handlers | Keep public method names stable while adapters are introduced |
| `src/include/functions.ini.php` config loading | Module-wide config bootstrap | Route shared module/config access through the new bootstrap helper first | This reduces repeated dirname lookups before broader refactors |
| Direct `icms_getmodulehandler()` calls throughout `src/` | Per-file handler lookup | Centralize lookup behind helper functions | Lets later refactors swap implementations in one place |
| Install and update hooks in `src/include/module.php` | Schema/bootstrap orchestration | Move schema-aware logic behind dedicated service-style helpers | Leave hook entry points intact for compatibility |
| Template-facing utility functions in `src/include/functions.php` | Mixed rendering and data access helpers | Separate presentation helpers from persistence access | Tackle after handler loading is consolidated |

## Migration guardrails

- Preserve the current module entry points and install/update hooks until handler migrations are complete.
- Refactor one handler family at a time so existing forum, topic, and post behavior stays unchanged.
- Prefer adding adapters and compatibility layers before deleting Art-based code.
- Expand CI coverage alongside each phase so new IPF-oriented code is checked on all supported PHP versions.
3 changes: 3 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
iForum is a full-featured discussion forum for ImpressCMS.

[Templates](templates.md)
[Art → IPF migration matrix](art-ipf-migration-matrix.md)
[PHP 8 compatibility checklist](php8-compatibility-checklist.md)
[Phased refactor roadmap](phased-refactor-roadmap.md)
27 changes: 27 additions & 0 deletions docs/phased-refactor-roadmap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Phased refactor roadmap

This roadmap describes the intended migration path while keeping the module installable and behaviorally stable.

## Phase 1: Developer safety nets

- Add CI coverage for PHP linting, coding standards, and static analysis.
- Document the Art → IPF migration path and PHP 8 review expectations.
- Introduce a shared bootstrap helper for module lookup and handler loading without wiring it into runtime paths yet.

## Phase 2: Shared access consolidation

- Replace duplicated module dirname and handler lookup code in low-risk files with the shared helper.
- Keep existing public entry points, hook signatures, and handler names unchanged.
- Add narrowly scoped validation to each migrated slice before widening coverage.

## Phase 3: Persistence layer migration

- Migrate one handler family at a time from Art-based patterns to IPF-friendly structures.
- Add adapters where needed so existing call sites keep working during the transition.
- Separate persistence concerns from presentation helpers as handlers move over.

## Phase 4: Cleanup and expansion

- Broaden coding standards and static analysis coverage once migrated areas are stable.
- Retire duplicate compatibility wrappers only after all call sites move to the shared abstractions.
- Update developer documentation as each migration phase lands.
23 changes: 23 additions & 0 deletions docs/php8-compatibility-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# PHP 8 compatibility checklist

Use this checklist when touching legacy iForum code while PHP 8.2 through PHP 8.5 remain supported in CI.

## Baseline checks

- Keep `parallel-lint` green across the full `src/` tree.
- Keep coding standards and static analysis green for the shared bootstrap helper and any newly added developer-facing files.
- Avoid introducing dynamic properties, removed functions, or deprecated string/array access patterns.

## Review checklist for changes

- Confirm `include_once` and `require_once` paths still resolve correctly when dirname helpers are touched.
- Check for implicit `null` to scalar conversions and loose comparisons that become noisier on newer PHP versions.
- Prefer explicit return values over relying on legacy truthy/falsy behavior.
- Avoid adding new references to removed PHP extensions or incompatible third-party tooling.
- Keep legacy global state access (`icms::$module`, config arrays, handler factories) wrapped behind helper functions where possible.

## Before merging refactors

- Re-run the PHP quality workflow on PHP 8.2, 8.3, 8.4, and 8.5.
- Verify install/update hooks still load through `src/icms_version.php` and `src/include/module.php`.
- Confirm helper-only refactors do not alter templates, routes, or persisted data.
12 changes: 12 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<ruleset name="iForum developer checks">
<description>Coding standards coverage for the shared bootstrap helper added in this refactor slice.</description>

<arg name="basepath" value="."/>
<arg name="colors"/>
<arg name="warning-severity" value="0"/>

<file>src/include/bootstrap.php</file>

<rule ref="PSR12"/>
</ruleset>
7 changes: 7 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
parameters:
level: 0
paths:
- src/include/bootstrap.php
bootstrapFiles:
- tools/phpstan-bootstrap.php
tmpDir: /tmp/phpstan-iforum
2 changes: 1 addition & 1 deletion src/class/art/functions.locale.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function xoops_local($func)
$msg = "<strong>The locale version is too old.</strong> Please copy <br />XOOPS/Frameworks/compat/language/english/<strong>local.php, local.class.php</strong> to XOOPS/language/english/";
if (icms::$config->getConfig("language") != "english") {
if (is_dir(ICMS_ROOT_PATH."/Frameworks/compat/language/".icms::$config->getConfig("language")."/")) {
$msg .= "<br />XOOPS/Frameworks/compat/language/".icms::$config->getConfig("language")."/<strong>local.php</strong> to XOOPS/language/".icms::$config->getConfig("language"]."/";
$msg .= "<br />XOOPS/Frameworks/compat/language/".icms::$config->getConfig("language")."/<strong>local.php</strong> to XOOPS/language/".icms::$config->getConfig("language")."/";
} else {
$msg .= "<br />And modify XOOPS/language/".icms::$config->getConfig("language")."/<strong>local.php</strong> according to XOOPS/language/english/local.php";
}
Expand Down
68 changes: 68 additions & 0 deletions src/include/bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

/**
* iForum - shared bootstrap helpers.
*
* @package iForum
*/

defined('ICMS_ROOT_PATH') or exit();

if (defined('IFORUM_BOOTSTRAP')) {
return;
}

define('IFORUM_BOOTSTRAP', 1);

function iforum_get_module_dirname()
{
static $moduleDirname;

if (!isset($moduleDirname)) {
$moduleDirname = basename(dirname(__FILE__, 2));
}

return $moduleDirname;
}

function iforum_get_module_path($path = '')
{
$modulePath = ICMS_ROOT_PATH . '/modules/' . iforum_get_module_dirname();

if ($path === '') {
return $modulePath;
}

return $modulePath . '/' . ltrim($path, '/');
}

function &iforum_get_module()
{
static $module;

if (isset($module)) {
return $module;
}

if (
isset(icms::$module)
&& is_object(icms::$module)
&& icms::$module->getVar('dirname', 'n') === iforum_get_module_dirname()
) {
$module = icms::$module;

return $module;
}

$moduleHandler = icms::handler('icms_module');
$module = $moduleHandler->getByDirname(iforum_get_module_dirname());

return $module;
}

function &iforum_get_handler($handlerName)
{
$handler = icms_getmodulehandler($handlerName, iforum_get_module_dirname(), 'iforum');

return $handler;
}
25 changes: 25 additions & 0 deletions tools/phpstan-bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

if (!class_exists('icms')) {
class icms
{
public static $module;

public static function handler($name)
{
return new class {
public function getByDirname($dirname)
{
return null;
}
};
}
}
}

if (!function_exists('icms_getmodulehandler')) {
function icms_getmodulehandler($name, $dirname, $module = '')
{
return null;
}
}
Loading