@@ -85,7 +85,7 @@ public function preserve() {
8585 $ relevant_paths = array ();
8686 foreach ($ this ->preservePaths as $ path ) {
8787 $ normalizedPath = $ this ->filesystem ->normalizePath ($ path );
88- if (file_exists ($ path ) && strpos ($ normalizedPath , $ installPathNormalized ) === 0 ) {
88+ if (static :: file_exists ($ path ) && strpos ($ normalizedPath , $ installPathNormalized ) === 0 ) {
8989 $ relevant_paths [] = $ normalizedPath ;
9090 }
9191 }
@@ -125,7 +125,7 @@ public function rollback() {
125125
126126 // Remove any code that was placed by the package at the place of
127127 // the original path.
128- if (file_exists ($ original )) {
128+ if (static :: file_exists ($ original )) {
129129 if (is_dir ($ original )) {
130130 $ this ->filesystem ->emptyDirectory ($ original , false );
131131 $ this ->filesystem ->removeDirectory ($ original );
@@ -137,14 +137,19 @@ public function rollback() {
137137 $ this ->io ->write (sprintf ('<comment>Files of installed package were overwritten with preserved path %s!</comment> ' , $ original ), true );
138138 }
139139
140- $ this ->filesystem ->ensureDirectoryExists (dirname ($ original ));
140+ $ folder = dirname ($ original );
141+ $ this ->filesystem ->ensureDirectoryExists ($ folder );
142+ // Make sure we can write the file to the folder.
143+ $ this ->makePathWritable ($ folder );
141144 $ this ->filesystem ->rename ($ backup_location , $ original );
142145
143146 if ($ this ->filesystem ->isDirEmpty (dirname ($ backup_location ))) {
144147 $ this ->filesystem ->removeDirectory (dirname ($ backup_location ));
145148 }
146149 }
147150
151+ // Restore all path permissions, that where set for the sake of moving
152+ // things around.
148153 $ this ->restorePathPermissions ();
149154
150155 // With a clean array, we can start over.
@@ -180,6 +185,12 @@ protected function preparePathPermissions($paths) {
180185 * @param string $path
181186 */
182187 protected function makePathWritable ($ path ) {
188+ // Make parent writable, before we can change the path itself.
189+ $ parent = dirname ($ path );
190+ if ($ parent != '. ' && !is_writable ($ parent )) {
191+ $ this ->makePathWritable ($ parent );
192+ }
193+
183194 $ this ->filepermissions [$ path ] = fileperms ($ path );
184195 chmod ($ path , static ::FILEPERM );
185196 }
@@ -198,4 +209,63 @@ protected function restorePathPermissions() {
198209 }
199210 }
200211
212+ /**
213+ * Check if file really exists.
214+ *
215+ * As php can only determine, whether a file or folder exists when the parent
216+ * directory is executable, we need to provide a workaround.
217+ *
218+ * @param $path
219+ * The path as in file_exists()
220+ *
221+ * @return bool
222+ * Returns TRUE if file exists, like in file_exists(),
223+ * but without restriction.
224+ *
225+ * @see file_exists()
226+ */
227+ static public function file_exists ($ path ) {
228+
229+ // Get all parent directories.
230+ $ folders = array ();
231+ $ reset_perms = array ();
232+ $ folder = $ path ;
233+ while ($ folder = dirname ($ folder )) {
234+ if ($ folder === '. ' || $ folder === '/ ' ) {
235+ break ;
236+ }
237+ elseif ($ folder === '' ) {
238+ continue ;
239+ }
240+ $ folders [] = $ folder ;
241+ }
242+
243+ foreach (array_reverse ($ folders ) as $ current_folder ) {
244+ // In the case a parent folder does not exist, the file cannot exist.
245+ if (!is_dir ($ current_folder )) {
246+ $ return = FALSE ;
247+ break ;
248+ }
249+ // In the case the folder is really a folder, but not executable, we need
250+ // to change that, so we can check if the file really exists.
251+ elseif (!is_executable ($ current_folder )) {
252+ $ reset_perms [$ current_folder ] = fileperms ($ current_folder );
253+ chmod ($ current_folder , 0755 );
254+ }
255+ }
256+
257+ if (!isset ($ return )) {
258+ $ return = file_exists ($ path );
259+ }
260+
261+ // Reset permissions in reverse order.
262+ foreach (array_reverse ($ reset_perms , TRUE ) as $ folder => $ mode ) {
263+ chmod ($ folder , $ mode );
264+ }
265+
266+ return $ return ;
267+ }
268+
269+
270+
201271}
0 commit comments