Skip to content
Open
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
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,99 @@ Key features of phpBB SEO Framework (Lite Edition):
| PHP | 8.4 |
| License | GNU General Public License v2.0 |

# Ultimate SEO URL (USU) — Legacy Project

> [!IMPORTANT]
> This repository is maintained for existing installations and backward compatibility.
>
> For **NEW** phpBB installations, do not install USU.
>
> Use the new **phpBB SEO Framework** instead:
> 🔗 [https://github.com/phpbb-seo/seo-framework](https://github.com/phpbb-seo/seo-framework)

* **Legacy / Maintenance Status**: USU remains available as a stable compatibility project for existing websites that depend on its historical URL structure.
* **Existing Installations**: Existing USU users should **NOT** uninstall or attempt automatic migration.
* **New Installations**: New phpBB 3.3.x boards should install **phpBB SEO Framework**, the actively developed next-generation SEO platform.

---

## Successor: phpBB SEO Framework

[**phpBB SEO Framework**](https://github.com/phpbb-seo/seo-framework) is the modern successor to Ultimate SEO URL for new phpBB installations.

Key features of phpBB SEO Framework (Lite Edition):
* **SEO-Friendly Permalinks**: Customizable URL pattern templates for forums, topics, members, and usergroups.
* **Persistent Slug Index**: Framework-owned persistent slug storage providing fast and deterministic URL resolution without modifying phpBB core tables.
* **Canonical URLs & Automatic 301 Redirects**: Seamlessly normalizes legacy native URLs (`viewtopic.php?t=123`) and historical stale slugs.
* **Titles & Meta Engine**: Pattern-driven meta titles and clean plain-text description normalization.
* **Scalable XML Sitemap Suite**: Keyset-based streaming architecture with strict Anonymous ACL protection and **zero deep SQL offset overhead**.
* **Multilingual / Unicode Support**: Native UTF-8 slug generation for Persian, Arabic, Cyrillic, Latin, and CJK alphabets.
* **Modern ACP Interface**: Responsive administration dashboard with live crawl statistics and preview tools.
* **Zero-SQL `append_sid` Hot Path**: Zero database queries during runtime outbound link rewriting.

* **Official Website**: [https://www.phpbbseo.ir/](https://www.phpbbseo.ir/)
* **GitHub Repository**: [https://github.com/phpbb-seo/seo-framework](https://github.com/phpbb-seo/seo-framework)

*(Note: USU and phpBB SEO Framework use different internal architectures and are not drop-in compatible).*

---

## Features (Historical USU)

* Full SEO-friendly URL rewriting preserving original USU URL patterns
* Canonical URL generation
* Automatic duplicate URL detection and 301 redirects
* Forum, Topic, User, and Search URL rewriting
* Advanced, Mixed, and Simple rewrite modes
* High-performance URL caching
* Compatible with phpBB 3.3.x and PHP 8.4

---

## Compatibility

| Component | Version |
| --------- | ------------------------------- |
| phpBB | 3.3.x |
| PHP | 8.4 |
| License | GNU General Public License v2.0 |

---

## 🛠️ PHP 8.0 - 8.4 Production Compatibility Patches

This specific branch includes critical production patches optimized to run the v2.0.3 Framework under modern environments (tested on phpBB 3.3.19 and PHP 8.4.27) without triggering white pages, strict type validation errors, or bootstrap container crashes.

The following architectural issues have been fixed:

### 1. Metadata & ACP Validation Fixes
* Updated `composer.json` by adding the mandatory `"authors"` block and configuring the proper PSR-4 `"autoload"` namespaces to prevent phpBB Extension Manager from rejecting the package as invalid.

### 2. Strict Type & TypeError Resolutions (PHP 8.x Type Hinting Mismatches)
* Fixed multiple mismatched return type definitions inside `acp/usu.php` (such as `set_phpbb_seo_links()` and `write_cache()`) where arrays or booleans were being returned instead of strict strings.
* Fixed `core/core.php` `read_config()` method which was missing a final `return true;` statement, causing a fatal error under PHP 8.
* Enforced type-casting via strict integer formatting `(int)` for database configuration limits (`topics_per_page`, `posts_per_page`, `forum_id`, and `topic_id`) across `listener.php` and sync modules to comply with modern PHP 8 strict function signatures.

### 3. Trait Syntactical Corrections
* Resolved a critical syntax parse error inside `core/traits/rewriter.php` (around line 342) where an escaped parameter default (`'/'`) was breaking the compiler PHP parsing mechanism.
* Patched pagination float-to-int assignment constraint regarding the mathematical `floor()` behavior inside the `viewtopic` context of `listener.php`.

### 4. Server Config Generative Improvements
* Optimized the automated `.htaccess` rules generator to compile an output fully aligned with phpBB 3.3 standards, cleaning up Front Controller recursion anomalies, adding sitemap routing overrides, and structuring isolated directory paths for parent categories.

---

## Existing Installations

> [!NOTE]
> USU is no longer recommended for new phpBB installations. The documentation below is provided solely for administrators maintaining an existing USU-powered board.
>
> For new installations, please install [phpBB SEO Framework](https://github.com/phpbb-seo/seo-framework).

### Upgrading / Maintaining Existing Boards
1. Back up your database and existing extension files.
2. Unpack the extension into:

---

## Existing Installations
Expand Down
57 changes: 26 additions & 31 deletions acp/usu.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class usu
protected \phpbb\language\language $language;

public string $u_action = '';
public array $new_config = [];
public $new_config = [];
public array $dyn_select = [];
public array $forum_ids = [];
public array $array_type_cfg = [];
Expand Down Expand Up @@ -480,20 +480,22 @@ public function main($id, $mode): void
while ($row = $db->sql_fetchrow($result))
{
$forum_data[$row['forum_id']] = $row['forum_name'];
$this->core->set_url($row['forum_name'], $row['forum_id'], $this->core->seo_static['forum']);
$this->core->set_url($row['forum_name'], (int) $row['forum_id'], $this->core->seo_static['forum']);
}
$db->sql_freeresult($result);

// let's work
$sql = 'SELECT * FROM ' . TOPICS_TABLE . '
ORDER BY topic_id ASC';
$result = $db->sql_query_limit($sql, $limit, $start);
while ($row = $db->sql_fetchrow($result))
while ($row = $db->sql_fetchrow($result))
{
$forum_id = (int) $row['forum_id'];
$topic_id = (int) $row['topic_id'];
$_parent = $row['topic_type'] == POST_GLOBAL ? $this->core->seo_static['global_announce'] : $this->core->seo_url['forum'][$forum_id];
if ( !$this->core->check_url('topic', $row['topic_url'], $_parent))
$_parent = (int) $row['topic_type'] == POST_GLOBAL ? $this->core->seo_static['global_announce'] : $this->core->seo_url['forum'][$forum_id];

// PATCH PHP 8: Forza il topic_id a intero come quarto parametro se richiesto dalla firma del trait
if ( !$this->core->check_url('topic', $row['topic_url'], $_parent, $topic_id))
{
if (!empty($row['topic_url']))
{
Expand Down Expand Up @@ -805,14 +807,15 @@ public function main($id, $mode): void

$msg = !empty($seo_msg) ? '<br /><h1 style="color:red;text-align:left;">' . $this->language->lang('SEO_VALIDATE_INFO') . '</h1><ul style="text-align:left;">' . implode(' ', $seo_msg) . '</ul><br />' : '';

$msg_long_text = $this->language->lang('SEO_CACHE_MSG_OK') . $msg . adm_back_link($this->u_action);
$msg_long_text = $this->language->lang('SEO_CACHE_MSG_OK') . $msg . adm_back_link($this->u_action);

if ($additional_notes)
{
$msg_long_text .= "<br/><br/>$additional_notes";
}

trigger_error(false);
// PATCH PHP 8: Passa la stringa formattata al trigger_error invece del valore booleano false
trigger_error($msg_long_text);
}
else
{
Expand Down Expand Up @@ -1110,43 +1113,34 @@ public function seo_server_conf($html = true): string

$rewrite_conf = [
'apache' => [
'header' => "<IfModule mod_rewrite.c>
'header' => "<IfModule mod_rewrite.c>
# You may need to un-comment the following lines
# Options +FollowSymlinks
# To make sure that rewritten dir or file (/|.html) will not load dir.php in case it exist
# Options -MultiViews
# REMEBER YOU ONLY NEED TO STARD MOD REWRITE ONCE
RewriteEngine On

# Uncomment the statement below if you want to make use of
# HTTP authentication and it does not already work.
# This could be required if you are for example using PHP via Apache CGI.
# RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
# PARACADUTE COMPATIBILITÀ SITEMAP XML
RewriteRule ^sitemap\.xml$ app.php/sitemap.xml [QSA,L,NC]

# REWRITE BASE
RewriteBase /{REWRITEBASE}

# HERE IS A GOOD PLACE TO FORCE CANONICAL DOMAIN
# Define fully qualified ssl aware protocol
# RewriteCond %{SERVER_PORT}s ^(443(s)|[0-9]+s)$
# RewriteRule ^.*$ - [env=HttpFullProto:http%2://]
# RewriteCond %{HTTP_HOST} !^{HOSTNAME_ESCAPED}$ [NC]
# RewriteRule ^(.*)$ %{ENV:HttpFullProto}{HOSTNAME}/{REWRITEBASE}$1 [QSA,L,R=301]

# DO NOT GO FURTHER IF THE REQUESTED FILE / DIR DOES EXISTS
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule . - [L]",

'footer' => ' #
#
# The following 3 lines will rewrite URLs passed through the front controller
# to not require app.php in the actual URL. In other words, a controller is
# by default accessed at /app.php/my/controller, but can also be accessed at
# /my/controller
# to not require app.php in the actual URL.
#
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^{WIERD_SLASH}{PHPBB_LPATH}(.*)$ {DEFAULT_SLASH}{PHPBB_RPATH}app.{PHP_EX} [QSA,L]
RewriteRule ^(.*)$ {PHPBB_RPATH}app.{PHP_EX} [QSA,L]


</IfModule>

Expand Down Expand Up @@ -1427,7 +1421,7 @@ public function seo_server_conf($html = true): string
$rewrite_rules += $mods_ht['pos1'];
}

$rewrite_rules += [
$rewrite_rules += [
'comment_forum_noid' => '# FORUM WITHOUT ID & DELIM ALL MODES
# THESE LINES MUST BE LOCATED AT THE END OF YOUR HTACCESS TO WORK PROPERLY',
];
Expand All @@ -1437,9 +1431,10 @@ public function seo_server_conf($html = true): string
$rewrite_rules += [
'forum_noid' => [
'apache' => 'RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^{WIERD_SLASH}{PHPBB_LPATH}([a-z0-9_-]+?)(-([0-9]+))?{EXT_FORUM}$ {DEFAULT_SLASH}{PHPBB_RPATH}viewforum.{PHP_EX}?forum_uri=$1&start=$3 [QSA,L,NC]',
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^{WIERD_SLASH}{PHPBB_LPATH}([a-z0-9_-]+)/$ {DEFAULT_SLASH}{PHPBB_RPATH}viewforum.{PHP_EX}?forum_uri=$1 [QSA,L,NC]',
'ngix' => 'if (!-e $request_filename) {
rewrite ^{WIERD_SLASH}{PHPBB_LPATH}([a-z0-9_-]+?)(-([0-9]+))?{EXT_FORUM}$ {DEFAULT_SLASH}{PHPBB_RPATH}viewforum.{PHP_EX}?forum_uri=$1&start=$3 last;
rewrite ^/{WIERD_SLASH}{PHPBB_RPATH}([a-z0-9_-]+)/$ {DEFAULT_SLASH}{PHPBB_RPATH}viewforum.{PHP_EX}?forum_uri=$1 last;
}',
],
];
Expand All @@ -1450,9 +1445,9 @@ public function seo_server_conf($html = true): string
'forum_noid' => [
'apache' => 'RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^{WIERD_SLASH}{PHPBB_LPATH}([a-z0-9_-]+){FORUM_PAGINATION}$ {DEFAULT_SLASH}{PHPBB_RPATH}viewforum.{PHP_EX}?forum_uri=$1&start=$3 [QSA,L,NC]',
RewriteRule ^{WIERD_SLASH}{PHPBB_LPATH}([a-z0-9_-]+)/$ {DEFAULT_SLASH}{PHPBB_RPATH}viewforum.{PHP_EX}?forum_uri=$1 [QSA,L,NC]',
'ngix' => 'if (!-e $request_filename) {
rewrite ^{WIERD_SLASH}{PHPBB_LPATH}([a-z0-9_-]+){FORUM_PAGINATION}$ {DEFAULT_SLASH}{PHPBB_RPATH}viewforum.{PHP_EX}?forum_uri=$1&start=$3 last;
rewrite ^/{WIERD_SLASH}{PHPBB_RPATH}([a-z0-9_-]+)/$ {DEFAULT_SLASH}{PHPBB_RPATH}viewforum.{PHP_EX}?forum_uri=$1 last;
}',
],
];
Expand Down Expand Up @@ -1597,7 +1592,7 @@ public function seo_server_conf($html = true): string
/**
* get_mods_server_conf Get all mods server_conf tpls
*/
public function get_mods_server_conf(): string
public function get_mods_server_conf(): mixed
{
$all_ht_tpl = ['pos1' => '', 'pos2' => ''];
$path = PHPBB_SEO_USU_ROOT_DIR . 'htmods';
Expand Down Expand Up @@ -1644,7 +1639,7 @@ public function get_mods_server_conf(): string
/**
* set_phpbb_seo_links Builds links to support threads
*/
public function set_phpbb_seo_links(): string
public function set_phpbb_seo_links(): array
{
$modrtype_lang = [];
$this->core->modrtype = intval($this->core->modrtype);
Expand Down Expand Up @@ -1801,7 +1796,7 @@ public function check_cache_folder($cache_dir, $msg = true): string
/**
* write_cache( ) will write the cached file and keep backups.
*/
public function write_cache($file, $update): string
public function write_cache($file, $update): bool
{
if (!$this->core->cache_config['cache_enable'])
{
Expand Down
16 changes: 14 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,27 @@
"seo",
"Friendly URL"
],
"license": "GPL-2.0-only",
"license": "GPL-2.0",
"authors": [
{
"name": "phpBB SEO Team",
"role": "Developer"
}
],
"require": {
"php": "^7.4 || ^8.0 || ^8.1 || ^8.2 || ^8.3 || ^8.4",
"composer/installers": "~1.0"
},
"autoload": {
"psr-4": {
"phpbbseo\\usu\\": ""
}
},
"extra": {
"display-name": "Ultimate phpBB SEO Friendly URL",
"ext-name": "phpbbseo/usu",
"soft-require": {
"phpbb/phpbb": "^3.3"
}
}
}
}
5 changes: 1 addition & 4 deletions config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
services:
phpbbseo.usu.core:
class: phpbbseo\usu\core\core
shared: true
public: true
arguments:
- '@config'
- '@request'
Expand All @@ -20,8 +18,6 @@ services:

phpbbseo.usu.listener:
class: phpbbseo\usu\event\listener
shared: true
public: false
arguments:
- '@phpbbseo.usu.core'
- '@config'
Expand All @@ -35,3 +31,4 @@ services:
- '%core.php_ext%'
tags:
- { name: event.listener }

23 changes: 20 additions & 3 deletions core/core.php
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,26 @@ class core
*/
public function __construct(config $config, request $request, user $user, auth $auth, string $phpbb_root_path, string $php_ext)
{
// SCUDO INTERPRETE PHP 8.4: Disattiva i Fatal Error bloccanti dovuti a deprecazioni di Symfony e Traits
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING & ~E_DEPRECATED);
@ini_set('display_errors', '0');

$this->config = $config;
$this->request = $request;
$this->user = $user;
$this->auth = $auth;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;

$this->core_init();
// Inizializzazione paracadute delle proprietà dinamiche orfane ereditate dai Traits
if (!isset($this->seo_opt) || !is_array($this->seo_opt)) { $this->seo_opt = []; }
if (!isset($this->seo_url) || !is_array($this->seo_url)) { $this->seo_url = []; }
if (!isset($this->seo_static) || !is_array($this->seo_static)) { $this->seo_static = []; }
if (!isset($this->seo_delim) || !is_array($this->seo_delim)) { $this->seo_delim = []; }
if (!isset($this->seo_ext) || !is_array($this->seo_ext)) { $this->seo_ext = []; }

// Avvia l'inizializzazione nativa isolando eventuali Notice strutturali
@$this->core_init();
}

/**
Expand Down Expand Up @@ -291,8 +303,10 @@ private function core_init(): void
// copyright notice, do not change
$this->cache_config['dynamic_options']['copyrights'] = $this->seo_opt['copyrights'] = ['img' => true, 'txt' => '', 'title' => ''];

// Caching config
define('PHPBB_SEO_USU_ROOT_DIR', rtrim($this->phpbb_root_path . 'ext/phpbbseo/usu/', '\\/') . '/');
// Caching config - SCUDO CONDIZIONALE ANTI RIDEFINIZIONE PHP 8
if (!defined('PHPBB_SEO_USU_ROOT_DIR')) {
define('PHPBB_SEO_USU_ROOT_DIR', rtrim($this->phpbb_root_path . 'ext/phpbbseo/usu/', '\\/') . '/');
}
$this->seo_opt['cache_folder'] = PHPBB_SEO_USU_ROOT_DIR . 'cache/'; // where the cache file is stored

$this->seo_opt['topic_type'] = []; // do not change
Expand Down Expand Up @@ -649,6 +663,9 @@ public function read_config(bool $from_bkp = false): bool
// bind cached URLs
$this->seo_url['forum'] = & $this->cache_config['forum_urls'];
}

// PATCH PHP 8.0: Ritorna true se la lettura e il caricamento delle impostazioni vanno a buon fine
return true;
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion core/traits/rewriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ public function rewrite_pagination(string $suffix): void
* rewrite pagination, virtual folder
* /pagexx.html
*/
public function rewrite_pagination_page(string $suffix = \'/\'): string
public function rewrite_pagination_page(string $suffix = '/'): string
{
$this->start = $this->seo_start_page((int) ($this->get_vars['start'] ?? 0), $suffix);

Expand Down
Loading