88use Amp \Cancellation ;
99use Amp \DeferredFuture ;
1010use Amp \File \File ;
11+ use Amp \File \Internal ;
12+ use Amp \File \LockType ;
1113use Amp \File \Whence ;
1214
1315/**
@@ -24,6 +26,8 @@ final class BlockingFile implements File, \IteratorAggregate
2426
2527 private readonly DeferredFuture $ onClose ;
2628
29+ private ?LockType $ lockType = null ;
30+
2731 /**
2832 * @param resource $handle An open filesystem descriptor.
2933 * @param string $path File path.
@@ -55,18 +59,46 @@ public function __destruct()
5559 }
5660 }
5761
58- public function read (?Cancellation $ cancellation = null , int $ length = self ::DEFAULT_READ_LENGTH ): ?string
62+ /**
63+ * Returns the currently active lock mode, or null if the file is not locked.
64+ */
65+ public function getLockType (): ?LockType
5966 {
60- if ($ this ->handle === null ) {
61- throw new ClosedException ("The file ' {$ this ->path }' has been closed " );
67+ return $ this ->lockType ;
68+ }
69+
70+ public function lock (LockType $ type , ?Cancellation $ cancellation = null ): void
71+ {
72+ Internal \lock ($ this ->path , $ this ->getFileHandle (), $ type , $ cancellation );
73+ $ this ->lockType = $ type ;
74+ }
75+
76+ public function tryLock (LockType $ type ): bool
77+ {
78+ $ locked = Internal \tryLock ($ this ->path , $ this ->getFileHandle (), $ type );
79+ if ($ locked ) {
80+ $ this ->lockType = $ type ;
6281 }
6382
83+ return $ locked ;
84+ }
85+
86+ public function unlock (): void
87+ {
88+ Internal \unlock ($ this ->path , $ this ->getFileHandle ());
89+ $ this ->lockType = null ;
90+ }
91+
92+ public function read (?Cancellation $ cancellation = null , int $ length = self ::DEFAULT_READ_LENGTH ): ?string
93+ {
94+ $ handle = $ this ->getFileHandle ();
95+
6496 try {
6597 \set_error_handler (function (int $ type , string $ message ): never {
6698 throw new StreamException ("Failed reading from file ' {$ this ->path }': {$ message }" );
6799 });
68100
69- $ data = \fread ($ this -> handle , $ length );
101+ $ data = \fread ($ handle , $ length );
70102 if ($ data === false ) {
71103 throw new StreamException ("Failed reading from file ' {$ this ->path }' " );
72104 }
@@ -79,16 +111,14 @@ public function read(?Cancellation $cancellation = null, int $length = self::DEF
79111
80112 public function write (string $ bytes ): void
81113 {
82- if ($ this ->handle === null ) {
83- throw new ClosedException ("The file ' {$ this ->path }' has been closed " );
84- }
114+ $ handle = $ this ->getFileHandle ();
85115
86116 try {
87117 \set_error_handler (function (int $ type , string $ message ): never {
88118 throw new StreamException ("Failed writing to file ' {$ this ->path }': {$ message }" );
89119 });
90120
91- $ length = \fwrite ($ this -> handle , $ bytes );
121+ $ length = \fwrite ($ handle , $ bytes );
92122 if ($ length === false ) {
93123 throw new StreamException ("Failed writing to file ' {$ this ->path }' " );
94124 }
@@ -131,6 +161,7 @@ public function close(): void
131161 throw new StreamException ("Failed closing file ' {$ this ->path }' " );
132162 } finally {
133163 \restore_error_handler ();
164+ $ this ->lockType = null ;
134165 }
135166 }
136167
@@ -146,16 +177,14 @@ public function onClose(\Closure $onClose): void
146177
147178 public function truncate (int $ size ): void
148179 {
149- if ($ this ->handle === null ) {
150- throw new ClosedException ("The file ' {$ this ->path }' has been closed " );
151- }
180+ $ handle = $ this ->getFileHandle ();
152181
153182 try {
154183 \set_error_handler (function (int $ type , string $ message ): never {
155184 throw new StreamException ("Could not truncate file ' {$ this ->path }': {$ message }" );
156185 });
157186
158- if (!\ftruncate ($ this -> handle , $ size )) {
187+ if (!\ftruncate ($ handle , $ size )) {
159188 throw new StreamException ("Could not truncate file ' {$ this ->path }' " );
160189 }
161190 } finally {
@@ -165,9 +194,7 @@ public function truncate(int $size): void
165194
166195 public function seek (int $ position , Whence $ whence = Whence::Start): int
167196 {
168- if ($ this ->handle === null ) {
169- throw new ClosedException ("The file ' {$ this ->path }' has been closed " );
170- }
197+ $ handle = $ this ->getFileHandle ();
171198
172199 $ mode = match ($ whence ) {
173200 Whence::Start => SEEK_SET ,
@@ -181,7 +208,7 @@ public function seek(int $position, Whence $whence = Whence::Start): int
181208 throw new StreamException ("Could not seek in file ' {$ this ->path }': {$ message }" );
182209 });
183210
184- if (\fseek ($ this -> handle , $ position , $ mode ) === -1 ) {
211+ if (\fseek ($ handle , $ position , $ mode ) === -1 ) {
185212 throw new StreamException ("Could not seek in file ' {$ this ->path }' " );
186213 }
187214
@@ -193,20 +220,12 @@ public function seek(int $position, Whence $whence = Whence::Start): int
193220
194221 public function tell (): int
195222 {
196- if ($ this ->handle === null ) {
197- throw new ClosedException ("The file ' {$ this ->path }' has been closed " );
198- }
199-
200- return \ftell ($ this ->handle );
223+ return \ftell ($ this ->getFileHandle ());
201224 }
202225
203226 public function eof (): bool
204227 {
205- if ($ this ->handle === null ) {
206- throw new ClosedException ("The file ' {$ this ->path }' has been closed " );
207- }
208-
209- return \feof ($ this ->handle );
228+ return \feof ($ this ->getFileHandle ());
210229 }
211230
212231 public function getPath (): string
@@ -238,4 +257,18 @@ public function getId(): int
238257 {
239258 return $ this ->id ;
240259 }
260+
261+ /**
262+ * @return resource
263+ *
264+ * @throws ClosedException
265+ */
266+ private function getFileHandle ()
267+ {
268+ if ($ this ->handle === null ) {
269+ throw new ClosedException ("The file ' {$ this ->path }' has been closed " );
270+ }
271+
272+ return $ this ->handle ;
273+ }
241274}
0 commit comments