Skip to content

Commit bacc240

Browse files
committed
feat: add support for stream_metadata operations with stream wrapper
1 parent 6cf9eec commit bacc240

File tree

4 files changed

+108
-4
lines changed

4 files changed

+108
-4
lines changed

src/CloudStorage/AbstractCloudStorageStreamWrapper.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,18 @@ public function stream_lock(): bool
332332
*
333333
* @see https://www.php.net/manual/en/streamwrapper.stream-metadata.php
334334
*/
335-
public function stream_metadata(): bool
335+
public function stream_metadata(string $path, int $option): bool
336336
{
337-
return false;
337+
return STREAM_META_TOUCH === $option ? $this->call(function () use ($path) {
338+
$client = $this->getClient();
339+
$key = $this->parsePath($path);
340+
341+
if (!$client->objectExists($key)) {
342+
$client->putObject($key, '', $this->getAcl());
343+
}
344+
345+
$this->removeCacheValue($path);
346+
}) : true;
338347
}
339348

340349
/**

tests/Integration/CloudStorage/AbstractCloudStorageStreamWrapperPhpTestCase.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,33 @@ public function testThrowsExceptionWhenContextHasNoClient()
496496
]));
497497
}
498498

499+
public function testTouchCreatesObjectIfItDoesntExist()
500+
{
501+
$this->client->expects($this->once())
502+
->method('objectExists')
503+
->with($this->identicalTo('/file.ext'))
504+
->willReturn(false);
505+
506+
$this->client->expects($this->once())
507+
->method('putObject')
508+
->with($this->identicalTo('/file.ext'), $this->identicalTo(''), $this->identicalTo($this->getAcl()));
509+
510+
$this->assertTrue(touch("{$this->getProtocol()}:///file.ext"));
511+
}
512+
513+
public function testTouchDoesntCreateObjectIfItExists()
514+
{
515+
$this->client->expects($this->once())
516+
->method('objectExists')
517+
->with($this->identicalTo('/file.ext'))
518+
->willReturn(false);
519+
520+
$this->client->expects($this->once())
521+
->method('putObject');
522+
523+
$this->assertTrue(touch("{$this->getProtocol()}:///file.ext"));
524+
}
525+
499526
public function testTruncatesFile()
500527
{
501528
$this->client->expects($this->once())

tests/Integration/CloudStorage/AbstractCloudStorageStreamWrapperS3TestCase.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,39 @@ public function testMkdirAndRmdir()
106106
$this->assertFalse($this->client->objectExists($directoryPath.'/'));
107107
}
108108

109+
public function testTouchCreatesEmptyFileIfFileDoesntExist()
110+
{
111+
$relativePath = '/'.basename(tempnam(sys_get_temp_dir(), 'ymir-').'.txt');
112+
$s3FilePath = "{$this->getProtocol()}://".$relativePath;
113+
114+
$this->assertFalse(file_exists($s3FilePath));
115+
116+
$this->assertTrue(touch($s3FilePath));
117+
118+
$this->assertTrue(file_exists($s3FilePath));
119+
$this->assertSame('', file_get_contents($s3FilePath));
120+
121+
$this->client->deleteObject($relativePath);
122+
}
123+
124+
public function testTouchDoesNothingIfFileExists()
125+
{
126+
$relativePath = '/'.basename(tempnam(sys_get_temp_dir(), 'ymir-').'.txt');
127+
$s3FilePath = "{$this->getProtocol()}://".$relativePath;
128+
129+
$this->assertFalse(file_exists($s3FilePath));
130+
131+
file_put_contents($s3FilePath, 'foo');
132+
133+
$this->assertTrue(file_exists($s3FilePath));
134+
135+
$this->assertTrue(touch($s3FilePath));
136+
137+
$this->assertSame('foo', file_get_contents($s3FilePath));
138+
139+
$this->client->deleteObject($relativePath);
140+
}
141+
109142
public function testTruncateExistingFile()
110143
{
111144
$relativePath = '/'.basename(tempnam(sys_get_temp_dir(), 'ymir-').'.txt');

tests/Unit/CloudStorage/AbstractCloudStorageStreamWrapperTestCase.php

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -643,9 +643,44 @@ public function testStreamLock()
643643
$this->assertFalse($this->getStreamWrapperObject()->stream_lock());
644644
}
645645

646-
public function testStreamMetadata()
646+
public function testStreamMetadataWithNonTouchOption()
647647
{
648-
$this->assertFalse($this->getStreamWrapperObject()->stream_metadata());
648+
$this->assertTrue($this->getStreamWrapperObject()->stream_metadata("{$this->getProtocol()}:///foo.txt", STREAM_META_GROUP));
649+
}
650+
651+
public function testStreamMetadataWithTouchOptionWhenObjectDoesntExist()
652+
{
653+
$client = $this->getCloudStorageClientInterfaceMock();
654+
655+
$client->expects($this->once())
656+
->method('objectExists')
657+
->with($this->identicalTo('/foo.txt'))
658+
->willReturn(false);
659+
660+
$client->expects($this->once())
661+
->method('putObject')
662+
->with($this->identicalTo('/foo.txt'), $this->identicalTo(''), $this->identicalTo($this->getAcl()));
663+
664+
$this->getStreamWrapperClass()::register($client, new \ArrayObject());
665+
666+
$this->assertTrue($this->getStreamWrapperObject()->stream_metadata("{$this->getProtocol()}:///foo.txt", STREAM_META_TOUCH));
667+
}
668+
669+
public function testStreamMetadataWithTouchOptionWhenObjectExists()
670+
{
671+
$client = $this->getCloudStorageClientInterfaceMock();
672+
673+
$client->expects($this->once())
674+
->method('objectExists')
675+
->with($this->identicalTo('/foo.txt'))
676+
->willReturn(true);
677+
678+
$client->expects($this->never())
679+
->method('putObject');
680+
681+
$this->getStreamWrapperClass()::register($client, new \ArrayObject());
682+
683+
$this->assertTrue($this->getStreamWrapperObject()->stream_metadata("{$this->getProtocol()}:///foo.txt", STREAM_META_TOUCH));
649684
}
650685

651686
public function testStreamOpenWithInvalidMode()

0 commit comments

Comments
 (0)