Skip to content

Prefix dependencies with PHP-Scoper to avoid class conflicts - #178

Open
jeremysimmons wants to merge 1 commit into
crowdsecurity:mainfrom
jeremysimmons:feat/php-scoper-vendor-isolation
Open

jeremysimmons wants to merge 1 commit into
crowdsecurity:mainfrom
jeremysimmons:feat/php-scoper-vendor-isolation

Conversation

@jeremysimmons

@jeremysimmons jeremysimmons commented Aug 28, 2026 •

Copy link
Copy Markdown

PHP-Scoper Vendor Isolation (in-repo, classmap-authoritative)

Context

  • vendor/ is committed; WP.org release deploys the repo tree (.github/workflows/release.yml, 10up SVN action). Scoping therefore happens in-repo and the scoped vendor is committed; release workflow is untouched.
  • Autoloader is required in crowdsec.php (line 20) and inc/bounce-current-ip.php (line 3) — paths stay the same.
  • Prefix: CrowdSec\Vendor.
  • PHP-Scoper runs as a dev-only PHAR on PHP 8.2+; shipped code keeps the PHP 7.2 floor.

Steps

1. Tooling (dev-only, gitignored)

  • Add bin/scope.sh (or composer script) that:
    1. rm -rf vendor && composer install --no-dev --prefer-dist (composer-patches applies patches)
    2. Downloads a pinned php-scoper.phar (gitignored) if missing
    3. php-scoper add-prefix --config=scoper.inc.php --output-dir=build/vendor-scoped vendor then replaces vendor/ contents with the scoped output
    4. composer dump-autoload --classmap-authoritative — classmap is built by scanning the actual (already-prefixed) class names, so no PSR-4 prefix mismatch and no runtime PSR-4 scanning
  • Add config.classmap-authoritative: true + optimize-autoloader: true to composer.json so any future dump stays classmap-only.
  • Gitignore php-scoper.phar and build/; add scoper.inc.php and bin/ to .distignore.

2. scoper.inc.php

  • prefix: 'CrowdSec\Vendor'
  • exclude-namespaces: Symfony\Polyfill (all polyfill packages ship global-function shims and must stay unprefixed)
  • exclude-files: polyfill bootstrap.php/bootstrap80.php files and symfony/deprecation-contracts/function.php (define global mb_*, ctype_*, trigger_deprecation)
  • expose-global-functions: true, expose-global-constants: true
  • Patcher for vendor/crowdsec/common/src/Logger/{FileLog,ConsoleLog}.php, which uses constant('Monolog\Level::…') — rewrite the string literal to the prefixed FQCN (PHP-Scoper doesn't touch it automatically)
  • During implementation, grep the scoped output for leftover unprefixed string FQCNs ('Symfony\\, 'Monolog\\, 'CrowdSec\\) and add patchers if any others surface

3. One-time rewrite of plugin source

4. Run the scope + commit

  • Execute the script, replace vendor/, dump the classmap-authoritative autoloader, commit scoped vendor + rewritten inc/.

5. Verify

  • php -l across inc/ and spot-check scoped vendor
  • Grep vendor/ for unprefixed namespace Symfony\Component, namespace Monolog, etc. (only polyfills should remain)
  • Confirm vendor/composer/autoload_static.php has the full prefixed classmap and PSR-4 maps are irrelevant (authoritative flag set)
  • Boot check: php -r "require 'vendor/autoload.php'; new CrowdSec\Vebdir\Monolog\Logger('t');" plus loading the plugin bootstrap if a local WP env is available

…licts

Isolate Symfony, Monolog, PSR, and CrowdSec SDK namespaces under
CrowdSec\Vendor using an authoritative classmap autoloader.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jeremysimmons
jeremysimmons force-pushed the feat/php-scoper-vendor-isolation branch from f4338cb to 32e0d6a Compare August 28, 2026 14:47
@jeremysimmons

Copy link
Copy Markdown
Author

DDEV site is up at https://wp69.ddev.site (/Users/jeremy/Code/wp-crowdsec-e2e). Admin: admin / admin123. WP 6.9, PHP 8.3.

Plugin was copied into wp-content/plugins/crowdsec (not a live symlink). Re-rsync after code changes.

Results (main suite, docker context)

File Result
1-activate-plugin pass (2)
2-live-mode-remediations pass (11)
3-live-mode-more pass (13)
4-live-mode-cache pass (2)
5-stream-mode pass (10)
6-redis pass (1)
7-memcached pass (1)
8-geolocation pass(3)
11-appsec pass (9)
12-appsec-timeout pass (4)
13-redis-acl pass (1)
14-blaas pass (9)

**66 passed ** Test 10 (auto_prepend) was not run — separate workflow.

@jeremysimmons
jeremysimmons marked this pull request as ready for review August 28, 2026 14:51
@julienloizelet

Copy link
Copy Markdown
Contributor

@jeremysimmons : Thanks for the PR ! Vendor prefixing with PHP Scoper would be a significant step forward.

I'm not working on this project right now. I'm letting the CrowdSec team take over ( hey @rr404 :) )

Thanks

@jeremysimmons

Copy link
Copy Markdown
Author

hey @rr404 - what do you need from me on this? can I provide any more description? is there more testing I need to do on this?

My motivation for this is fixing the performance with the class loader. PSR-4 is "expensive" on the wordpress host I use, wpengine. they have opcode and apcu caching turned off, so things aren't as fast as they ought to be.

@jeremysimmons

Copy link
Copy Markdown
Author

@julienloizelet @rr404 Anything I can do to nudge this along?

@jeremysimmons

Copy link
Copy Markdown
Author

@AlteredCoder, @he2ss, @blotus you're listed as people on https://github.com/crowdsecurity
Do any of you know who the maintainer is for this repository?

@jeremysimmons

Copy link
Copy Markdown
Author

@julienloizelet @rr404 @AlteredCoder, @he2ss, @blotus any idea who I can talk to about this? I'm a real human...

@rr404

rr404 commented Nov 4, 2026

Copy link
Copy Markdown

Hey, sorry for the late reply I missed the notifs.

I'll have a look today

@rr404

rr404 commented Nov 4, 2026

Copy link
Copy Markdown

Looks ok and not breaking things.
Finishing test hopefully tomorrow merge

@jeremysimmons

Copy link
Copy Markdown
Author

amaze amaze. thank you!

@jeremysimmons

Copy link
Copy Markdown
Author

resolves #174!

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.

3 participants