1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
38: namespace MOC\Core\Drive\File;
39: use MOC\Api;
40: use MOC\Core\Drive;
41: use MOC\Core\Error;
42: 43: 44:
45: class Write extends Property {
46:
47: 48: 49: 50: 51: 52:
53: public static function InterfaceDepending() {
54: return Api::Core()->Depending();
55: }
56:
57: 58: 59: 60: 61: 62:
63: public static function InterfaceInstance() {
64: return new Write();
65: }
66:
67: 68: 69: 70: 71: 72:
73: public static function InterfaceChangelog() {
74: return Api::Core()->Changelog()->Create( __CLASS__ );
75: }
76:
77: const MODE_APPEND = 'a';
78: const MODE_WRITE = 'w';
79: const MODE_WRITE_BINARY = 'wb';
80:
81: 82: 83: 84: 85: 86: 87:
88: public function Copy( $Location ) {
89: if( file_exists( $this->Location() ) ) {
90: if( copy( $this->Location(), $Location ) ) {
91: return true;
92: }
93: }
94: return false;
95: }
96: 97: 98: 99: 100: 101: 102:
103: public function Save( $Mode = self::MODE_WRITE_BINARY ) {
104: $Mode = $this->WriteMode( $Mode );
105: if( is_array( $this->Content ) ) {
106: if( !$this->lockWrite( $this->Location(), implode( PHP_EOL, $this->Content ), $Mode ) ) {
107: return false;
108: }
109: } else {
110: if( !$this->lockWrite( $this->Location(), $this->Content, $Mode ) ) {
111: return false;
112: }
113: }
114: $this->UpdateProperties();
115: $this->Changed( false );
116: return true;
117: }
118:
119: 120: 121: 122: 123: 124: 125: 126:
127: public function SaveAs( $Location, $Mode = self::MODE_WRITE_BINARY ) {
128: $Mode = $this->WriteMode( $Mode );
129: if( is_array( $this->Content ) ) {
130: if( !$this->lockWrite( $Location, implode( PHP_EOL, $this->Content ), $Mode ) ) {
131: return false;
132: }
133: } else {
134: if( !$this->lockWrite( $Location, $this->Content, $Mode ) ) {
135: return false;
136: }
137: }
138: $this->Location( $Location );
139: $this->UpdateProperties();
140: $this->Changed( false );
141: return true;
142: }
143:
144: 145: 146: 147: 148: 149: 150:
151: public function Move( $Location ) {
152: if( file_exists( $this->Location() ) ) {
153: if( ($Return = $this->lockRename( $Location )) ) {
154: $this->Location( $Location );
155: $this->UpdateProperties();
156: }
157: return $Return;
158: }
159: return false;
160: }
161:
162: 163: 164: 165: 166:
167: public function Remove() {
168: if( file_exists( $this->Location() ) ) {
169: return $this->lockRemove();
170: }
171: return false;
172: }
173:
174: 175: 176: 177: 178:
179: public function Touch() {
180: if( strlen( $this->Location() ) > 0 ) {
181: fclose( fopen( $this->Location(), 'a' ) );
182: $this->UpdateProperties();
183: return true;
184: } else {
185: return false;
186: }
187: }
188:
189: 190: 191: 192: 193: 194: 195: 196:
197: private function lockWrite( $Location = null, $Content = null, $Mode = null ) {
198: $Mode = $this->WriteMode( $Mode );
199: switch( strtoupper( $Mode ) ) {
200: case 'A': {
201: if( ( $Handler = fopen( $this->Location(), 'a' ) ) !== false ) {
202: if( fwrite( $Handler, $Content ) === false ) {
203: return false;
204: }
205: if( fclose( $Handler ) === false ) {
206: return false;
207: }
208: return true;
209: }
210: return false;
211: break;
212: }
213: default: {
214:
215:
216: $CacheFile = Drive::InterfaceInstance()->File()->Handle( $this->Cache() );
217: if( ( $CacheHandler = fopen( $CacheFile->Location(), $Mode ) ) === false ) {
218: Error::InterfaceInstance()->Type()->Exception()->Trigger( 'Cache-Access failed!' );
219: }
220:
221: $CacheTimeout = 15;
222: while( flock( $CacheHandler, LOCK_EX | LOCK_NB ) === false && $CacheTimeout > 0 ) {
223: usleep( round( rand( 1,1000 )*1000 ) );
224: $CacheTimeout--;
225: }
226: if( ! $CacheTimeout > 0 ) {
227: Error::InterfaceInstance()->Type()->Exception()->Trigger( 'Cache-Lock failed!' );
228: }
229: if( fwrite( $CacheHandler, $Content ) === false ) {
230: Error::InterfaceInstance()->Type()->Exception()->Trigger( 'Cache-Write failed!' );
231: }
232:
233: if( flock( $CacheHandler, LOCK_UN ) === false ) {
234: Error::InterfaceInstance()->Type()->Exception()->Trigger( 'Cache-UnLock failed!' );
235: }
236: if( fclose( $CacheHandler ) === false ) {
237: Error::InterfaceInstance()->Type()->Exception()->Trigger( 'Cache-Close failed!' );
238: }
239: $Timeout = 15;
240: while( ( $Check = $this->lockRemove() ) === false && $Timeout > 0 ) {
241: usleep( round( rand( 1,1000 )*1000 ) );
242: $Timeout--;
243: }
244: if( $Check === false ) {
245: Error::InterfaceInstance()->Type()->Exception()->Trigger( 'File-UnLink failed!' );
246: }
247:
248: $Timeout = 15;
249: while( ( $Check = $CacheFile->lockRename( $Location ) ) === false && $Timeout > 0 ) {
250: usleep( round( rand( 1,1000 )*1000 ) );
251: $Timeout--;
252: }
253: if( $Check === false ) {
254: Error::InterfaceInstance()->Type()->Exception()->Trigger( 'File-Write failed!' );
255: }
256: return true;
257: }
258: }
259: }
260:
261: 262: 263: 264: 265: 266:
267: private function lockRename( $Location, $Timeout = 15 ) {
268: if( is_file( $this->Location() ) ) {
269: if(
270: ( false !== ( $HandlerA = fopen( $this->Location(), "r" ) ) )
271: && ( false !== ( $HandlerB = fopen( $Location, "w" ) ) )
272: ){
273: $TimeoutA = $TimeoutB = $Timeout;
274: while( flock( $HandlerA, LOCK_EX | LOCK_NB ) === false && $TimeoutA > 0 ) {
275: usleep( round( rand( 1,1000 )*1000 ) );
276: $TimeoutA--;
277: }
278: if( $TimeoutA > 0 ) {
279: while( flock( $HandlerB, LOCK_EX | LOCK_NB ) === false && $TimeoutB > 0 ) {
280: usleep( round( rand( 1,1000 )*1000 ) );
281: $TimeoutB--;
282: }
283: if( $TimeoutB > 0 ) {
284: flock( $HandlerA, LOCK_UN );
285: fclose( $HandlerA );
286: flock( $HandlerB, LOCK_UN );
287: fclose( $HandlerB );
288: return rename( $this->Location(), $Location );
289: }
290: }
291: flock( $HandlerA, LOCK_UN );
292: fclose( $HandlerA );
293: fclose( $HandlerB );
294: }
295: }
296: return false;
297: }
298:
299: 300: 301: 302: 303:
304: private function lockRemove( $Timeout = 15 ) {
305: if( is_file( $this->Location() ) ) {
306: if( false !== ( $Handler = fopen( $this->Location(), "w" ) ) ) {
307: while( flock( $Handler, LOCK_EX | LOCK_NB ) === false && $Timeout > 0 ) {
308: usleep( round( rand( 1,1000 )*1000 ) );
309: $Timeout--;
310: }
311: if( $Timeout > 0 ) {
312: flock( $Handler, LOCK_UN );
313: fclose( $Handler );
314: return unlink( $this->Location() );
315: }
316: fclose( $Handler );
317: }
318: } else {
319: return true;
320: }
321: return false;
322: }
323:
324: 325: 326: 327: 328: 329:
330: private function Cache() {
331: if( ( $CacheFile = tempnam( ini_get('upload_tmp_dir'), 'write' ) ) === false ) {
332: Error::InterfaceInstance()->Type()->Exception()->Trigger( 'Cache-Access failed!' );
333: }
334: return $CacheFile;
335: }
336:
337: 338: 339: 340: 341: 342: 343:
344: private function WriteMode( $Mode ) {
345: switch( $Mode ) {
346: case 1: { return self::MODE_APPEND; }
347: case 2: { return self::MODE_WRITE; }
348: case 3: { return self::MODE_WRITE_BINARY; }
349: case self::MODE_APPEND: { return self::MODE_APPEND; }
350: case self::MODE_WRITE: { return self::MODE_WRITE; }
351: case self::MODE_WRITE_BINARY: { return self::MODE_WRITE_BINARY; }
352: default: { return self::MODE_WRITE_BINARY; }
353: }
354: }
355: }
356: