Installation:
composer require league/flysystem-phpcr
Ensure you have a PHPCR-compatible repository (e.g., Jackrabbit, eZ Publish, or Doctrine PHPCR-ODM) configured.
Basic Usage:
use League\Flysystem\Filesystem;
use League\Flysystem\Phpcr\PhpcrAdapter;
$repository = new \Doctrine\ODM\PHPCR\DocumentManager(); // or your PHPCR repo
$adapter = new PhpcrAdapter($repository, '/path/in/repo');
$filesystem = new Filesystem($adapter);
// Upload a file
$filesystem->write('file.txt', 'Hello, PHPCR!');
// Read a file
$contents = $filesystem->read('file.txt');
First Use Case:
$localFilesystem = new Filesystem(new \League\Flysystem\Local\LocalFilesystemAdapter('/local/path'));
$remoteFilesystem = new Filesystem($adapter);
foreach ($localFilesystem->listContents('', true) as $file) {
$remoteFilesystem->write($file['path'], $localFilesystem->read($file['path']));
}
File Operations:
$filesystem->write('documents/report.pdf', file_get_contents('local.pdf'));
$content = $filesystem->read('documents/report.pdf');
file_put_contents('local_copy.pdf', $content);
$filesystem->writeStream('large_file.zip', fopen('local.zip', 'r'));
$stream = $filesystem->readStream('large_file.zip');
Metadata Handling:
$filesystem->write('file.txt', 'Content', [
'timestamp' => '2023-01-01T00:00:00+0000', // Custom timestamp
'custom:property' => 'value', // PHPCR-specific metadata
]);
Directory Operations:
$filesystem->createDir('subfolder');
$filesystem->deleteDir('subfolder');
foreach ($filesystem->listContents('subfolder') as $item) {
if ($item['type'] === 'file') {
// Handle file
}
}
Symlinks (if supported by PHPCR):
$filesystem->createSymlink('target.txt', 'link.txt');
Laravel Storage:
Extend Laravel’s Filesystem class to support PHPCR:
namespace App\Storage;
use League\Flysystem\Filesystem;
use League\Flysystem\Phpcr\PhpcrAdapter;
use Illuminate\Contracts\Filesystem as FilesystemContract;
class PhpcrStorage implements FilesystemContract {
protected $filesystem;
public function __construct($repository, $rootPath = '/') {
$this->filesystem = new Filesystem(new PhpcrAdapter($repository, $rootPath));
}
// Delegate all methods to $this->filesystem
public function write($path, $contents, $options = []) {
return $this->filesystem->write($path, $contents, $options);
}
// ... other methods (read, delete, etc.)
}
Register in config/filesystems.php:
'phpcr' => [
'driver' => 'phpcr',
'repository' => function () {
return app('phpcr.document_manager');
},
'root' => '/laravel-storage',
],
Event Handling: Use PHPCR’s event system to trigger actions (e.g., logging, notifications) when files are modified:
$repository->getEventDispatcher()->addListener(
'prePersist',
function ($event) {
$document = $event->getDocument();
if ($document instanceof \PHPCR\NodeInterface) {
logger()->info("File created: {$document->getPath()}");
}
}
);
Caching: Cache file metadata or contents if PHPCR operations are slow:
$cache = new \Symfony\Component\Cache\Adapter\FilesystemAdapter();
$filesystem->read('file.txt', function () use ($cache) {
return $cache->get('file:file.txt', function () {
return $filesystem->read('file.txt');
});
});
PHPCR-Specific Behavior:
nt:file, nt:folder). The adapter handles this, but invalid operations (e.g., writing to a non-nt:file node) may fail silently or throw exceptions.$repository->beginTransaction();
try {
$filesystem->write('file.txt', 'Content');
$repository->commit();
} catch (\Exception $e) {
$repository->rollback();
throw $e;
}
Metadata Limitations:
lastModified() may return PHPCR’s internal timestamp unless configured otherwise (see release 1.1.0).jcr:mixinTypes, custom:property).Performance:
$repository->getWorkspace()->getNode('/')->setProperty('batch:flag', true);
Archived Package:
Enable PHPCR Logging: Configure Doctrine PHPCR-ODM or your PHPCR client to log queries:
$repository->getConfiguration()->setQueryLogger(new \Doctrine\ODM\PHPCR\Logging\QueryLogger());
Or use Monolog:
$logger = new \Monolog\Logger('phpcr');
$logger->pushHandler(new \Monolog\Handler\StreamHandler('phpcr.log'));
$repository->getConfiguration()->setLogger($logger);
Check Node Existence:
Verify nodes exist before operations to avoid NodeNotFoundException:
if (!$filesystem->has('file.txt')) {
$filesystem->write('file.txt', 'Content');
}
Handle Encoding:
PHPCR may return binary data or encoded strings. Use mb_convert_encoding if needed:
$content = mb_convert_encoding($filesystem->read('file.txt'), 'UTF-8', 'auto');
Custom Adapter Configuration:
Extend PhpcrAdapter to add repository-specific logic:
class CustomPhpcrAdapter extends PhpcrAdapter {
public function write($path, $contents, array $options = []) {
// Add custom logic (e.g., pre-process contents)
$contents = $this->preProcess($contents);
return parent::write($path, $contents, $options);
}
protected function preProcess($contents) {
// Example: Compress contents
return gzcompress($contents);
}
}
Override Metadata:
Modify how metadata is handled by overriding getMetadata or setMetadata:
class CustomPhpcrAdapter extends PhpcrAdapter {
public function getMetadata($path) {
$metadata = parent::getMetadata($path);
$metadata['custom'] = $this->getCustomMetadata($path);
return $metadata;
}
}
Add PHPCR-Specific Features: Expose PHPCR features not covered by Flysystem (e.g., versioning, access control):
$adapter->getPhpcrNode('/path')->getProperty('jcr:created');
Fallback for Missing Features: Implement fallback logic for unsupported Flysystem methods:
public function createSymlink($target, $link) {
throw new \League\Flysystem\FilesystemException("
How can I help you explore Laravel packages today?