Install
$ agentstack add skill-saadrahman01-claude-moodle-dev-moodle-upgrade-migration ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Moodle Upgrade & Migration
Overview
Moodle deprecates aggressively but rarely removes. Code from Moodle 2.x often still runs in 4.5 — but uses APIs that emit warnings, fail PHP 8 strict checks, or block plugin directory acceptance. This skill catalogs the migration path from each generation.
When to Use
- Upgrading a plugin to support a newer Moodle version
- Resolving deprecation warnings
- Migrating to PHP 8.1 / 8.2 / 8.3 / 8.4
- Plugin directory submission rejection ("uses deprecated API")
- Cross-version compat (
$plugin->requiresbump)
Strategy
- Bump
$plugin->requiresto your minimum target Moodle version - Replace deprecated APIs (table below)
- Add
upgrade.txtto document changes - Run
phpcs --standard=moodle+ visual smoke test - Bump
$plugin->version+ adddb/upgrade.phpstep if schema changed
Deprecation table
| Old | New | Since | |-----|-----|-------| | print_error('errkey', 'comp') | throw new \moodle_exception('errkey', 'comp') | 3.7 | | add_to_log() | Trigger an event class | 2.7 | | notify('text', 'notifyproblem') | \core\notification::error('text') | 3.0 | | redirect($url, 'msg', 0) | redirect($url, 'msg', null, \core\notification::INFO) | 3.0 | | external_api (bare) | \core_external\external_api | 4.2 | | external_function_parameters etc. | \core_external\... | 4.2 | | $mform->setHelpButton() | $mform->addHelpButton() | 2.0 | | Magic callbacks _extend_navigation | Hooks API \core\hook\... (where applicable) | 4.4 | | \core\session\manager::write_close() | Same — but check if needed | — | | mtrace() in non-CLI | Use debugging() or proper logger | — | | cleardoubleslashes | Use clean_param($url, PARAM_URL) | — | | addslashes() for DB | Use $DB->... placeholders | 2.0 | | mysql_* functions | $DB always | 2.0 | | userdate($t, '', false) (no timezone) | Pass timezone or use core_date::get_user_timezone_object() | 3.2 | | create_function() | Closures function() { } | PHP 7.2 removed | | each($array) | foreach | PHP 7.2 removed | | assert($string) | Real expression | PHP 7.2+ | | (unset) cast | Plain unset() | PHP 7.2+ | | \Mustache_Engine direct | $OUTPUT->render_from_template | — | | cm_info::get_modinfo second arg | API change in 3.4 | 3.4 | | format_text no context | Always pass ['context' => $context] | 2.0 | | \html_writer::nonempty_tag | Use html_writer::tag with check | 3.5 | | \stdClass w/o : \stdClass return type in PHP 8.1+ | Add return types | 8.1 |
PHP version migrations
→ PHP 8.0
each(),create_function()removed'string' . nullwarns — explicit cast- Negative string offset behavior changed
- Named arguments — Moodle prohibits in public APIs (positional only)
→ PHP 8.1
- Implicit nullable parameters
(string $x = null)deprecated →(?string $x = null) Returning by reference from a void function- Internal classes
#[\AllowDynamicProperties]if you set undeclared properties tempnam,parse_urlminor signature changes
→ PHP 8.2
- Dynamic properties on user classes deprecated
${...}string interpolation deprecated →{$...}utf8_encode/utf8_decodedeprecated
→ PHP 8.3
#[\Override]attribute encouragedDate_Create_From_Formatstrict-ness- Typed class constants supported
→ PHP 8.4
- Implicit nullable params now hard-deprecated:
function f(string $x = null)→function f(?string $x = null) - Optional param before required param deprecated — reorder so optionals come last
E_STRICTconstant removed (was already a no-op)trigger_error(..., E_USER_ERROR)deprecated — throw an exception instead- CSV functions:
fputcsv()/fgetcsv()/str_getcsv()default$escapedeprecated — pass''explicitly to opt out of legacy escape xml_set_*_handlerstring-callable form deprecated — pass[$obj, 'method']or first-class callablemysqli_kill(),mysqli_refresh(),mysqli_ping()deprecated — irrelevant to Moodle ($DB layer), but flag in custom codeDatePeriodISO8601 string constructor deprecated →DatePeriod::createFromISO8601String()mb_trim(),mb_ltrim(),mb_rtrim()added — prefer over manual regex trimming for multibyte
Moodle 3.x → 4.x
Theme
- 3.x default theme
Cleanremoved; use Boost - 4.0 introduced course index, secondary navigation, drawers
- Layouts:
frontpage,mydashboard,incourse,coursemay need overrides
Hooks API (4.4+)
Old:
function local_example_extend_navigation(global_navigation $nav) { /* ... */ }
New (where supported):
// db/hooks.php
$callbacks = [
[
'hook' => \core\hook\navigation\primary_extend::class,
'callback' => '\local_example\hooks\navigation::extend_primary',
],
];
// classes/hooks/navigation.php
namespace local_example\hooks;
class navigation {
public static function extend_primary(\core\hook\navigation\primary_extend $hook): void {
$hook->get_primary_view()->add(/* ... */);
}
}
Magic callbacks still work in 4.4+; Hooks API is preferred for new code, required for some new extension points.
External API namespace
// Pre-4.2
class get_items extends external_api { /* ... */ }
// 4.2+
use core_external\external_api;
use core_external\external_function_parameters;
class get_items extends external_api { /* ... */ }
Compatibility shim: 4.2+ aliases bare names to namespaced for back-compat — but new code should use namespaced.
Course format API
format_ plugins migrated from format_base to \core_courseformat\base:
// classes/output/courseformat/content.php
namespace format_yourname\output\courseformat;
class content extends \core_courseformat\output\local\content { /* ... */ }
3.x format plugins need full rewrite for 4.0+.
Moodle 4.x → 5.x
5.0 (2025-04)
- PHP 8.2 minimum
- Bootstrap 5 fully replaces Bootstrap 4 utility classes
.sr-only→.visually-hidden.float-left→.float-start.ml-*→.ms-*data-toggle→data-bs-toggle- Some 4.x deprecations finalize
- New AI subsystem (
\core_ai)
5.1 (2025-10-06)
- PHP 8.2 min, 8.3 & 8.4 supported. Sodium ext required. 64-bit only.
max_input_vars≥ 5000. /publicdocument root. Web server must point at/public. Plugins still live above (/local/...,/mod/...) — installer relocates; manual moves needed for in-place upgrades.- Routing Engine (optional, BC-safe) — new request dispatcher + cleaner URLs.
- Deprecations:
file_encode_url()deprecated → usemoodle_url::make_pluginfile_url()- Device-related theme functions final-deprecated
- Quiz callback classes/functions deprecated (see
mod/quiz/UPGRADING.md) course/changenumsections.phppage removed- Course "max sections" setting removed
- DB prefix max length now 10 chars.
- Always check per-component
UPGRADING.md(5.1 replacesupgrade.txtfor API change notes).
5.2 (2026-04-20)
- PHP 8.3 min, 8.4 supported.
- Upgrade path: must come from 4.4 or later. Older → upgrade through 4.4/5.0/5.1 first.
- Oracle DB removed. Minimums bumped: PostgreSQL 16, MySQL 8.4, MariaDB 10.11, SQL Server 2019.
- React in core — base library available via importmaps; new UI code may use React components.
- Composer support for third-party library installation in plugins.
- OpenTelemetry integration.
- Final deprecations:
core/modal_factory,core/modal_registry(AMD) — usecore/modaldirectly- Pre-PHP 7 style constructors removed (no method named after class)
- Everything in
lib/deprecatedlib.phpfrom ≤ 4.4 removed - New/expanded web services:
site_info,mobile_config,choice_results.
Always check /public/lib/upgrade.txt (path changed in 5.1) and per-component UPGRADING.md for the target version.
upgrade.txt convention
/upgrade.txt:
This files describes API changes in /local/example/*,
information provided here is intended especially for developers.
=== 1.5.0 ===
* The deprecated function local_example_old_thing() has been removed. Use local_example_new_thing() instead.
* New \local_example\hooks\foo class for the navigation hook (Moodle 4.4+).
=== 1.4.0 ===
* New web service local_example_export_csv.
Mirrors Moodle's own lib/upgrade.txt. Plugin directory reviewers read it.
db/upgrade.php cumulative example
get_manager();
if ($oldversion field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_plugin_savepoint(true, 2024010100, 'local', 'example');
}
if ($oldversion index_exists($table, $index)) {
$dbman->drop_index($table, $index);
}
upgrade_plugin_savepoint(true, 2024050100, 'local', 'example');
}
if ($oldversion get_recordset_select('local_example_items', 'metadata IS NULL');
foreach ($rs as $r) {
$r->metadata = json_encode([]);
$DB->update_record('local_example_items', $r);
}
$rs->close();
upgrade_plugin_savepoint(true, 2025010100, 'local', 'example');
}
return true;
}
Each if block targets one historical version. Never delete past blocks — sites may upgrade through multiple versions.
Compatibility shim pattern
For plugins supporting multiple Moodle versions:
if (class_exists('\core_external\external_api')) {
class_alias('\core_external\external_api', '\local_example\compat\external_api');
} else {
class_alias('\external_api', '\local_example\compat\external_api');
}
Or guard with \core\plugin_manager::get_remote_plugin_info and moodle_major_version():
if (version_compare(moodle_major_version(), '4.4', '>=')) {
// Hooks API path
} else {
// Magic callback fallback
}
Common Mistakes
| Mistake | Fix | |---------|-----| | Bumping $plugin->requires without testing on min version | Test against $plugin->requires Moodle release | | Removing db/upgrade.php blocks | Keep all historical blocks | | Skipping upgrade_plugin_savepoint | Required — marks step as done | | Adding new field without field_exists guard | Use guard in case site is mid-upgrade | | Forgetting upgrade.txt | Plugin directory reviewers expect it | | Removing deprecated function used by other plugins | Mark deprecated for one major, then remove | | Using PHP 8.2 features without bumping $plugin->requires Moodle | Match Moodle's PHP min | | print_error left after migration | Replace with throw new moodle_exception | | external_api namespace mismatch breaking 4.1 | Use compatibility alias |
Tools
# Find deprecated API usage
phpcs --standard=moodle --report=summary local/example
moodle-cs/standards/Moodle/...
# Find PHP version issues
phpcompatinfo analyser:run local/example
phpstan analyse local/example --level=5
References
- Plugin upgrade flow: https://moodledev.io/docs/guides/migrating
- API deprecations: https://moodledev.io/general/development/policies/deprecation
- upgrade.txt format: https://moodledev.io/general/development/policies/codingstyle#upgrade
- Hooks API: https://moodledev.io/docs/apis/core/hooks
- Per-version notes: https://moodledev.io/general/releases
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: SaadRahman01
- Source: SaadRahman01/claude-moodle-dev
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.