PHP Dependency Scoping
Overview
The News app uses dependency scoping to isolate its PHP dependencies and prevent conflicts with other Nextcloud apps. All vendor dependencies are prefixed with the OCA\News\Vendor namespace and stored in lib/Vendor/ instead of the standard vendor/ directory.
Why Scoping?
Problem: When multiple Nextcloud apps use the same PHP library (e.g., different versions of feed-io), PHP cannot handle duplicate class declarations, leading to fatal errors.
Solution: Each app gets its own isolated copy of dependencies with a unique namespace prefix, preventing conflicts.
How It Works
Scoping Process
When you run composer scope-dependencies:
- Install php-scoper (in isolation via bamarni/composer-bin-plugin)
- Scan dependencies using
composer show --tree --no-dev - Prefix namespaces with
OCA\News\Vendorusing php-scoper - Organize files into
lib/Vendor/directory structure - Generate autoloader for scoped dependencies
Configuration Files
- scoper.inc.php: Defines which dependencies to scope and excludes PSR standards
- lib-vendor-organizer.php: Moves scoped code from
build/tolib/Vendor/ - vendor-bin/php-scoper/composer.json: Declares php-scoper dependency
Usage
Building the App
# Install dependencies and run scoping
make build
# Or manually
composer install --no-dev
composer scope-dependencies
Development
# Install dev dependencies with scoping
composer install
composer scope-dependencies
# Run tests (automatically scopes if needed)
make test
Scoped Dependencies
All these are scoped under OCA\News\Vendor:
- feed-io - RSS/Atom feed parsing
- Favicon - Favicon fetching
- fivefilters/Readability - Article extraction
- League/Uri - URI manipulation
- GuzzleHttp/Psr7 - PSR-7 HTTP messages
- php-http/ - HTTP client adapter
- And all their transitive dependencies
Exception: Psr\Log is NOT scoped as it's a standard PSR interface.
Implementation Details
Makefile Integration
make buildautomatically runs scoping after composer installmake testchecks if scoping is needed before running testsmake appstorepackages scoped dependencies correctly
Appstore Package
The appstore target:
- Copies lib/ with scoped dependencies
- Removes original vendor files (now scoped in lib/Vendor/)
- Keeps vendor/autoload.php and vendor/composer/
- Includes composer.json for autoload configuration
- Cleans up test files and .git directories from scoped deps
Code Updates
All imports in lib/ and tests/ use the scoped namespace:
// Before
use FeedIo\FeedIo;
use Favicon\Favicon;
// After
use OCA\News\Vendor\FeedIo\FeedIo;
use OCA\News\Vendor\Favicon\Favicon;
Guzzle Compatibility
Guzzle is provided by Nextcloud server and is not scoped. The FeedIoClient class wraps unscoped PSR-7 responses into scoped ones for compatibility with scoped feed-io.
Benefits
✅ No Conflicts - Apps can use different versions of the same library
✅ Independent - News doesn't affect other apps
✅ Maintainable - Clear namespace prefixing pattern
✅ Standard - Uses php-scoper, a well-maintained tool