fix: cast string returns to int in MY_Migration by jekkos · Pull Request #4560 · opensourcepos/opensourcepos · GitHub
Skip to content

fix: cast string returns to int in MY_Migration#4560

Merged
jekkos merged 1 commit into
masterfrom
fix/4559-migration-type-error
May 22, 2026
Merged

fix: cast string returns to int in MY_Migration#4560
jekkos merged 1 commit into
masterfrom
fix/4559-migration-type-error

Conversation

@jekkos

@jekkos jekkos commented May 22, 2026

Copy link
Copy Markdown
Member

Summary

  • Cast basename() return to (int) in get_latest_migration()basename() always returns a string, but the method declares int return type
  • Cast $result->version to (int) in get_current_version() — database column values are strings, but the method declares int return type

Fixes #4559

Root Cause

PHP 8.0+ enforces strict return types. On older PHP versions, strings were silently coerced to int. On PHP 8.4 (as used in the Docker image), basename() returning "20240101000000" and $result->version returning a string from MariaDB both throw TypeError when the function declares : int as return type.

This affects fresh installs most severely because get_latest_migration() is called early in the migration setup flow.

How to Test

  • Fresh install on PHP 8.4 with MariaDB (the reported scenario)
  • Verify login page loads without TypeError
  • Verify migration runs successfully

Summary by CodeRabbit

  • Bug Fixes
    • Improved migration version handling to ensure consistent data type processing, preventing potential issues with version comparison and migration execution.

Review Change Stack

basename() returns string and database column values are strings,
but get_latest_migration() and get_current_version() declare int
return types. PHP 8.0+ enforces strict return types and no longer
silently coerces strings to int, causing a TypeError on fresh
installs.

Fixes #4559
@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Libraries/MY_Migration.php`:
- Line 28: get_latest_migration() is using basename() unnecessarily on the
migration version from MigrationRunner::findMigrations(); replace return (int)
basename(end($migrations)->version) with just casting the version: return (int)
end($migrations)->version, and optionally add a guard that checks if $migrations
is empty before calling end() to avoid errors (refer to get_latest_migration(),
MigrationRunner::findMigrations(), and getMigrationNumber()).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 47db6c62-6c94-41cc-8ab4-e85feb9ba04e

📥 Commits

Reviewing files that changed from the base of the PR and between b738429 and de144d4.

📒 Files selected for processing (1)
  • app/Libraries/MY_Migration.php

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify migration object structure and basename usage

# Search for findMigrations method implementation in CodeIgniter
rg -n -A 20 'function findMigrations' --type=php

# Look for how migration version property is set
rg -n -B 3 -A 3 '->version\s*=' --type=php -g 'Migration*.php'

# Check other usages of migration version in the codebase
rg -n 'basename.*->version|->version.*basename' --type=php

Repository: opensourcepos/opensourcepos

Length of output: 586


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file around the relevant methods
echo "== MY_Migration.php (around get_latest_migration / findMigrations usage) =="
nl -ba app/Libraries/MY_Migration.php | sed -n '1,120p'

# Locate the get_latest_migration method definition (in case line numbers shifted)
echo
echo "== get_latest_migration definition =="
rg -n "function get_latest_migration" app/Libraries/MY_Migration.php
rg -n "function findMigrations" app/Libraries/MY_Migration.php

# Find findMigrations implementation anywhere in the repo (CodeIgniter / app overrides)
echo
echo "== findMigrations implementations in the repo =="
rg -n "function findMigrations" --type=php

# Check whether any codebase path parses migration versions with basename() or expects version as int already
echo
echo "== basename usage with migration version =="
rg -n --type=php "basename\\(.*->version|->version.*basename" .

Repository: opensourcepos/opensourcepos

Length of output: 208


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== File size =="
wc -l app/Libraries/MY_Migration.php

echo
echo "== MY_Migration.php (first 220 lines with line numbers) =="
awk '{printf "%6d %s\n", NR, $0}' app/Libraries/MY_Migration.php | sed -n '1,220p'

echo
echo "== get_latest_migration location =="
rg -n "function get_latest_migration" app/Libraries/MY_Migration.php
rg -n "function get_current_version" app/Libraries/MY_Migration.php
rg -n "function findMigrations" app/Libraries/MY_Migration.php

echo
echo "== findMigrations implementations across repo =="
rg -n "function findMigrations" --type=php .

echo
echo "== Usage of basename with migration version nearby (repo-wide) =="
rg -n --type=php "basename\\(.*->version|->version.*basename" .

Repository: opensourcepos/opensourcepos

Length of output: 5633


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Search for MigrationRunner::findMigrations implementation =="
rg -n "function findMigrations" --type=php .

echo
echo "== Search for findMigrations() call sites =="
rg -n "findMigrations\\(" --type=php .

echo
echo "== Search for MigrationRunner class file =="
rg -n "class MigrationRunner" --type=php .

echo
echo "== Inspect CodeIgniter MigrationRunner file if found =="
CODEFILE="$(rg -l "class MigrationRunner" --type=php . | head -n 1 || true)"
echo "First match: ${CODEFILE:-<none>}"
if [ -n "${CODEFILE:-}" ]; then
  wc -l "$CODEFILE"
  sed -n '1,240p' "$CODEFILE" | head -n 240
  # show around findMigrations if exists
  rg -n "function findMigrations" "$CODEFILE" || true
  if rg -q "function findMigrations" "$CODEFILE"; then
    line="$(rg -n "function findMigrations" "$CODEFILE" | head -n 1 | cut -d: -f1)"
    start=$((line-20)); end=$((line+60))
    echo
    echo "== Snippet around findMigrations in $CODEFILE (lines $start-$end) =="
    sed -n "${start},${end}p" "$CODEFILE"
  fi
fi

echo
echo "== Repo-wide basename(...->version) patterns =="
rg -n --type=php "basename\\(.*->version|->version.*basename" .

Repository: opensourcepos/opensourcepos

Length of output: 135


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Top-level dirs (look for vendor/) =="
ls -la | head

echo
echo "== Does vendor/ exist? =="
if [ -d vendor ]; then
  echo "vendor exists"
  ls -la vendor | head
else
  echo "vendor missing"
fi

echo
echo "== Find MigrationRunner in repo (all file types) =="
fd -H "MigrationRunner.php" . || true
fd -H "MigrationRunner" . || true

echo
echo "== Search for class MigrationRunner (PHP) =="
rg -n --type=php "class MigrationRunner" . || true

echo
echo "== Search for findMigrations symbol (PHP) =="
rg -n --type=php "findMigrations" . || true

echo
echo "== How get_latest_migration() output used (is_latest) =="
rg -n --type=php "is_latest\\(" app/Libraries/MY_Migration.php || true

Repository: opensourcepos/opensourcepos

Length of output: 1643


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Search vendor for findMigrations =="
rg -n --type=php "findMigrations" vendor || true

echo
echo "== Search vendor for class MigrationRunner =="
rg -n --type=php "class MigrationRunner" vendor || true

echo
echo "== Find MigrationRunner.php files =="
fd -H "MigrationRunner.php" vendor || true
fd -H "MigrationRunner" vendor || true

echo
echo "== Show candidate MigrationRunner file contents (snippets) =="
for f in $(fd -H "MigrationRunner" vendor -e php 2>/dev/null || true); do
  echo "--- $f ---"
  rg -n "function findMigrations" "$f" && rg -n "findMigrations" "$f" || true
done

Repository: opensourcepos/opensourcepos

Length of output: 258


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== vendor top-level =="
ls -ლა vendor

echo
echo "== vendor/codeigniter4 (tree up to depth 4) =="
find vendor/codeigniter4 -maxdepth 4 -type d | head -n 200

echo
echo "== Search vendor for 'MigrationRunner' (any file) =="
rg -n --hidden --no-ignore --glob='*.php' "MigrationRunner" vendor | head -n 50 || true

echo
echo "== Search vendor for 'findMigrations' (any file) =="
rg -n --hidden --no-ignore --glob='*.php' "findMigrations" vendor | head -n 50 || true

echo
echo "== Find any file names containing 'Migration' under vendor (depth-limited) =="
fd -H -t f -e php "Migration" vendor --max-depth 6 2>/dev/null | head -n 200 || true

echo
echo "== List possible CodeIgniter migration library files =="
fd -H -t f "migration" vendor --max-depth 6 2>/dev/null | head -n 200 || true

Repository: opensourcepos/opensourcepos

Length of output: 176


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ls -la vendor =="
ls -la vendor | head

echo
echo "== list vendor/codeigniter4 (depth <= 6) =="
find vendor/codeigniter4 -maxdepth 6 -type d | head -n 200

echo
echo "== rg class MigrationRunner in vendor (php only) =="
rg -n --type=php "class MigrationRunner" vendor || true

echo
echo "== rg findMigrations in vendor (php only) =="
rg -n --type=php "findMigrations" vendor || true

echo
echo "== fd MigrationRunner.php under vendor (if any) =="
fd -H -t f "MigrationRunner.php" vendor || true

echo
echo "== fd *Migration* under vendor (depth <= 8) =="
fd -H -t f -e php "*Migration*" vendor --max-depth 8 | head -n 200 || true

Repository: opensourcepos/opensourcepos

Length of output: 9153


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Search vendor/codeigniter4 for MigrationRunner/findMigrations (case-insensitive) =="
rg -ni --type=php "migrationrunner" vendor/codeigniter4 || true
rg -ni --type=php "findmigrations" vendor/codeigniter4 || true

echo
echo "== Search vendor/codeigniter for MigrationRunner/findMigrations (case-insensitive) =="
rg -ni --type=php "migrationrunner" vendor/codeigniter || true
rg -ni --type=php "findmigrations" vendor/codeigniter || true

echo
echo "== List files under vendor/codeigniter4 that look like migration runner =="
fd -t f -i "migrationrunner" vendor/codeigniter4 || true
fd -t f -i "migration" vendor/codeigniter4/framework/system/Database --max-depth 5 || true
fd -t f -i "migration" vendor/codeigniter4/framework/system --max-depth 6 || true

echo
echo "== List files under vendor/codeigniter4 that contain 'Migrations' =="
fd -t f -i "*migrations*" vendor/codeigniter4/framework/system --max-depth 8 || true

Repository: opensourcepos/opensourcepos

Length of output: 1178


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="vendor/codeigniter4/framework/system/Database/MigrationRunner.php"
echo "== file =="
wc -l "$FILE"
echo

echo "== locate findMigrations() =="
rg -n "function findMigrations" "$FILE"
echo

echo "== snippet around findMigrations() =="
line="$(rg -n "function findMigrations" "$FILE" | head -n 1 | cut -d: -f1)"
start=$((line-40)); end=$((line+120))
sed -n "${start},${end}p" "$FILE" | head -n 220

echo
echo "== findMigrations return types / end($migrations) patterns in CI =="
rg -n "findMigrations|basename\\(.*->version|end\\(\\$migrations\\)" "$FILE"

Repository: opensourcepos/opensourcepos

Length of output: 4698


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="vendor/codeigniter4/framework/system/Database/MigrationRunner.php"

echo "== locate getMigrationNumber() =="
rg -n "function getMigrationNumber" "$FILE"

echo
line="$(rg -n "function getMigrationNumber" "$FILE" | head -n 1 | cut -d: -f1)"
start=$((line-40)); end=$((line+80))
sed -n "${start},${end}p" "$FILE"

echo
echo "== locate regex for migration filename numbering =="
rg -n "protected \\$regex|\\$regex\\s*=" "$FILE" || true

echo
echo "== show usage of basename() in CI MigrationRunner =="
rg -n --type=php "basename\\(" "$FILE" || true

echo
echo "== repo-wide basename( ... ->version ) =="
rg -n --type=php "basename\\(.*->version|->version.*basename" . || true

Repository: opensourcepos/opensourcepos

Length of output: 3134


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Migration directories under app/ =="
fd -t d -i "migrations" app --max-depth 6 || true

echo
echo "== List a few migration php files under app/Database/Migrations (if present) =="
fd -t f -e php "Database/Migrations" app --max-depth 8 || true
fd -t f -e php -i "migrations" app --max-depth 10 | head -n 30 || true

echo
echo "== Show sample migration filenames (first 20) =="
files=$(fd -t f -e php -i "migrations" app --max-depth 10 || true)
printf "%s\n" "$files" | head -n 20

Repository: opensourcepos/opensourcepos

Length of output: 318


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== app/Config/Migrations.php =="
wc -l app/Config/Migrations.php
sed -n '1,200p' app/Config/Migrations.php

echo
echo "== Search repo for migration files (*.php) under *Database/Migrations* =="
fd -t f -e php -i "database/migrations" . --max-depth 12 || true

echo
echo "== Search repo for files matching *Migrations/*<number>* and .php =="
fd -t f -e php -i "Migrations" . --max-depth 20 | head -n 200 || true

Repository: opensourcepos/opensourcepos

Length of output: 2458


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== find migration scripts under *Database/Migrations* =="
find . -type f -path "*Database/Migrations*" -name "*.php" | head -n 50

echo
echo "== find migration-script-like filenames (timestampFormat YmdHis_ / Y-m-d-His_ / Y_m_d_His_) =="
find . -type f -name "20*_*.php" | head -n 50
find . -type f -name "20*_.php" | head -n 50

echo
echo "== find files matching pattern *YmdHis_* or similar =="
# (best-effort: look for directories named Migrations and files starting with 20)
find . -type f -path "*Migrations*" -name "20*.php" | head -n 50

Repository: opensourcepos/opensourcepos

Length of output: 9250


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --type=php "basename\\([^)]*->version" . || true
rg -n --type=php "->version[^)]*basename" . || true

Repository: opensourcepos/opensourcepos

Length of output: 476


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Search for basename(...->version) patterns =="
rg -n --type=php "basename\\([^)]*->version" . || true

echo
echo "== Search for ->version...basename patterns =="
rg -n --type=php -- "->version[^)]*basename" . || true

Repository: opensourcepos/opensourcepos

Length of output: 168


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="vendor/codeigniter4/framework/system/Database/MigrationRunner.php"
echo "== MigrationRunner properties (top of file) =="
sed -n '1,120p' "$FILE" | head -n 120

echo
echo "== locate constructor / default namespace/path =="
rg -n "function __construct|\\$namespace|\\$path" "$FILE" | head -n 80

Repository: opensourcepos/opensourcepos

Length of output: 2580


Remove redundant basename() around the migration version in get_latest_migration() (and optionally harden for empty discovery).

  • MigrationRunner::findMigrations() already sets $migration->version to the migration number extracted from the migration filename (getMigrationNumber()), so basename(end($migrations)->version) is unnecessary/unclear—use (int) end($migrations)->version instead.
  • end($migrations) would only error if findMigrations() returns an empty array (no matching migration files / namespace mismatch). In this repo there are many matching migrations under app/Database/Migrations/, so this is likely safe; a guard is optional hardening.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Libraries/MY_Migration.php` at line 28, get_latest_migration() is using
basename() unnecessarily on the migration version from
MigrationRunner::findMigrations(); replace return (int)
basename(end($migrations)->version) with just casting the version: return (int)
end($migrations)->version, and optionally add a guard that checks if $migrations
is empty before calling end() to avoid errors (refer to get_latest_migration(),
MigrationRunner::findMigrations(), and getMigrationNumber()).

@jekkos jekkos merged commit 5450404 into master May 22, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Fresh install migration type error

1 participant