33namespace React \Filesystem \Uv ;
44
55use React \EventLoop \ExtUvLoop ;
6+ use React \EventLoop \Loop ;
67use React \Filesystem \Node \FileInterface ;
78use React \Filesystem \PollInterface ;
89use React \Promise \Promise ;
1112
1213final class File implements FileInterface
1314{
15+ // private const READ_CHUNK_FIZE = 65536;
16+ private const READ_CHUNK_FIZE = 1 ;
17+
1418 use StatTrait;
1519
1620 private ExtUvLoop $ loop ;
@@ -36,41 +40,102 @@ public function stat(): PromiseInterface
3640 public function getContents (int $ offset = 0 , ?int $ maxlen = null ): PromiseInterface
3741 {
3842 $ this ->activate ();
39- return new Promise (function (callable $ resolve ) use ($ offset , $ maxlen ): void {
40- uv_fs_open ($ this ->uvLoop , $ this ->path . DIRECTORY_SEPARATOR . $ this ->name , UV ::O_RDONLY , 0 , function ($ fileDescriptor ) use ($ resolve , $ offset , $ maxlen ): void {
41- uv_fs_fstat ($ this ->uvLoop , $ fileDescriptor , function ($ fileDescriptor , array $ stat ) use ($ resolve , $ offset , $ maxlen ): void {
42- uv_fs_read ($ this ->uvLoop , $ fileDescriptor , $ offset , $ maxlen ?? (int )$ stat ['size ' ], function ($ fileDescriptor , string $ buffer ) use ($ resolve ): void {
43- $ resolve ($ buffer );
44- uv_fs_close ($ this ->uvLoop , $ fileDescriptor , function () {
45- $ this ->deactivate ();
43+ return $ this ->openFile (
44+ $ this ->path . DIRECTORY_SEPARATOR . $ this ->name ,
45+ UV ::O_RDONLY ,
46+ 0 ,
47+ )->then (
48+ function ($ fileDescriptor ) use ($ offset , $ maxlen ): PromiseInterface {
49+ $ buffer = '' ;
50+ $ bufferLength = 0 ;
51+ $ read = function (bool $ finalAttempt , int $ offset ) use ($ fileDescriptor , $ maxlen , &$ read , &$ buffer , &$ bufferLength ): PromiseInterface {
52+ return new Promise (function (callable $ resolve ) use ($ fileDescriptor , $ offset , $ maxlen , $ finalAttempt , &$ read , &$ buffer , &$ bufferLength ): void {
53+ \uv_fs_read ($ this ->uvLoop , $ fileDescriptor , $ offset , $ maxlen ?? self ::READ_CHUNK_FIZE , function ($ fileDescriptor , string $ contents ) use ($ resolve , $ maxlen , $ finalAttempt , &$ read , &$ buffer , &$ bufferLength ): void {
54+ $ contentLength = strlen ($ contents );
55+ $ buffer .= $ contents ;
56+ $ bufferLength += $ contentLength ;
57+
58+ if (
59+ ($ maxlen === null && $ finalAttempt ) ||
60+ ($ maxlen !== null && $ bufferLength >= $ maxlen )
61+ ) {
62+ if ($ maxlen !== null && $ bufferLength > $ maxlen ) {
63+ $ buffer = substr ($ buffer , 0 , $ maxlen );
64+ }
65+
66+ $ resolve ($ this ->closeOpenFile ($ fileDescriptor )->then (function () use ($ buffer ): string {
67+ $ this ->deactivate ();
68+
69+ return $ buffer ;
70+ }));
71+ } else if ($ maxlen === null && !$ finalAttempt && $ contentLength === 0 ) {
72+ $ resolve ($ read (true , $ bufferLength ));
73+ } else {
74+ $ resolve ($ read (false , $ bufferLength ));
75+ }
4676 });
4777 });
48- });
49- });
50- });
78+ };
79+
80+ return $ read (false , $ offset );
81+ }
82+ );
5183 }
5284
5385 public function putContents (string $ contents , int $ flags = 0 )
5486 {
5587 $ this ->activate ();
56- return new Promise ( function ( callable $ resolve ) use ( $ contents , $ flags ): void {
57- uv_fs_open (
58- $ this -> uvLoop ,
59- $ this -> path . DIRECTORY_SEPARATOR . $ this -> name ,
60- (( $ flags & \ FILE_APPEND ) == \ FILE_APPEND ) ? UV :: O_RDWR | UV :: O_CREAT | UV :: O_APPEND : UV :: O_RDWR | UV :: O_CREAT ,
61- 0644 ,
62- function ($ fileDescriptor ) use ($ resolve , $ contents , $ flags ): void {
88+ return $ this -> openFile (
89+ $ this -> path . DIRECTORY_SEPARATOR . $ this -> name ,
90+ (( $ flags & \ FILE_APPEND ) == \ FILE_APPEND ) ? UV :: O_RDWR | UV :: O_CREAT | UV :: O_APPEND : UV :: O_RDWR | UV :: O_CREAT ,
91+ 0644 ,
92+ )-> then (
93+ function ( $ fileDescriptor ) use ( $ contents ): PromiseInterface {
94+ return new Promise ( function (callable $ resolve ) use ($ contents , $ fileDescriptor ): void {
6395 uv_fs_write ($ this ->uvLoop , $ fileDescriptor , $ contents , 0 , function ($ fileDescriptor , int $ bytesWritten ) use ($ resolve ): void {
64- $ resolve ($ bytesWritten );
65- uv_fs_close ($ this ->uvLoop , $ fileDescriptor , function () {
96+ $ resolve ($ this ->closeOpenFile ($ fileDescriptor )->then (function () use ($ bytesWritten ): int {
6697 $ this ->deactivate ();
67- });
98+ return $ bytesWritten ;
99+ }));
68100 });
69101 }
70102 );
71103 });
72104 }
73105
106+ private function openFile (string $ path , int $ flags , int $ mode ): PromiseInterface
107+ {
108+ $ this ->activate ();
109+ return new Promise (function (callable $ resolve ) use ($ path , $ flags , $ mode ): void {
110+ uv_fs_open (
111+ $ this ->uvLoop ,
112+ $ this ->path . DIRECTORY_SEPARATOR . $ this ->name ,
113+ $ flags ,
114+ $ mode ,
115+ function ($ fileDescriptor ) use ($ resolve ): void {
116+ $ this ->deactivate ();
117+ $ resolve ($ fileDescriptor );
118+ }
119+ );
120+ });
121+ }
122+
123+ private function closeOpenFile ($ fileDescriptor ): PromiseInterface
124+ {
125+ $ this ->activate ();
126+ return new Promise (function (callable $ resolve ) use ($ fileDescriptor ) {
127+ try {
128+ uv_fs_close ($ this ->uvLoop , $ fileDescriptor , function () use ($ resolve ) {
129+ $ this ->deactivate ();
130+ $ resolve ();
131+ });
132+ } catch (\Throwable $ error ) {
133+ $ this ->deactivate ();
134+ throw $ error ;
135+ }
136+ });
137+ }
138+
74139 public function unlink (): PromiseInterface
75140 {
76141 $ this ->activate ();
0 commit comments