@@ -23,7 +23,61 @@ class AddLinesConfigurator extends AbstractConfigurator
2323 self ::POSITION_AFTER_TARGET ,
2424 ];
2525
26+ /**
27+ * Holds file contents for files that have been loaded.
28+ * This allows us to "change" the contents of a file multiple
29+ * times before we actually write it out.
30+ *
31+ * @var string[]
32+ */
33+ private $ fileContents = [];
34+
2635 public function configure (Recipe $ recipe , $ config , Lock $ lock , array $ options = []): void
36+ {
37+ $ this ->fileContents = [];
38+ $ this ->executeConfigure ($ recipe , $ config );
39+
40+ foreach ($ this ->fileContents as $ file => $ contents ) {
41+ $ this ->write (sprintf ('[add-lines] Patching file "%s" ' , $ this ->relativize ($ file )));
42+ file_put_contents ($ file , $ contents );
43+ }
44+ }
45+
46+ public function unconfigure (Recipe $ recipe , $ config , Lock $ lock ): void
47+ {
48+ $ this ->fileContents = [];
49+ $ this ->executeUnconfigure ($ recipe , $ config );
50+
51+ foreach ($ this ->fileContents as $ file => $ change ) {
52+ $ this ->write (sprintf ('[add-lines] Reverting file "%s" ' , $ this ->relativize ($ file )));
53+ file_put_contents ($ file , $ change );
54+ }
55+ }
56+
57+ public function update (RecipeUpdate $ recipeUpdate , array $ originalConfig , array $ newConfig ): void
58+ {
59+ // manually check for "requires", as unconfigure ignores it
60+ $ originalConfig = array_filter ($ originalConfig , function ($ item ) {
61+ return !isset ($ item ['requires ' ]) || $ this ->isPackageInstalled ($ item ['requires ' ]);
62+ });
63+
64+ // reset the file content cache
65+ $ this ->fileContents = [];
66+ $ this ->executeUnconfigure ($ recipeUpdate ->getOriginalRecipe (), $ originalConfig );
67+ $ this ->executeConfigure ($ recipeUpdate ->getNewRecipe (), $ newConfig );
68+ $ newFiles = [];
69+ $ originalFiles = [];
70+ foreach ($ this ->fileContents as $ file => $ contents ) {
71+ // set the original file to the current contents
72+ $ originalFiles [$ this ->relativize ($ file )] = file_get_contents ($ file );
73+ // and the new file where the old recipe was unconfigured, and the new configured
74+ $ newFiles [$ this ->relativize ($ file )] = $ contents ;
75+ }
76+ $ recipeUpdate ->addOriginalFiles ($ originalFiles );
77+ $ recipeUpdate ->addNewFiles ($ newFiles );
78+ }
79+
80+ public function executeConfigure (Recipe $ recipe , $ config ): void
2781 {
2882 foreach ($ config as $ patch ) {
2983 if (!isset ($ patch ['file ' ])) {
@@ -57,8 +111,6 @@ public function configure(Recipe $recipe, $config, Lock $lock, array $options =
57111 continue ;
58112 }
59113
60- $ this ->write (sprintf ('Patching file "%s" ' , $ patch ['file ' ]));
61-
62114 if (!isset ($ patch ['position ' ])) {
63115 $ this ->write (sprintf ('The "position" key is required for the "add-lines" configurator for recipe "%s". Skipping ' , $ recipe ->getName ()));
64116
@@ -78,11 +130,12 @@ public function configure(Recipe $recipe, $config, Lock $lock, array $options =
78130 }
79131 $ target = isset ($ patch ['target ' ]) ? $ patch ['target ' ] : null ;
80132
81- $ this ->patchFile ($ file , $ content , $ position , $ target , $ warnIfMissing );
133+ $ newContents = $ this ->getPatchedContents ($ file , $ content , $ position , $ target , $ warnIfMissing );
134+ $ this ->fileContents [$ file ] = $ newContents ;
82135 }
83136 }
84137
85- public function unconfigure (Recipe $ recipe , $ config, Lock $ lock ): void
138+ public function executeUnconfigure (Recipe $ recipe , $ config ): void
86139 {
87140 foreach ($ config as $ patch ) {
88141 if (!isset ($ patch ['file ' ])) {
@@ -106,51 +159,17 @@ public function unconfigure(Recipe $recipe, $config, Lock $lock): void
106159 }
107160 $ value = $ patch ['content ' ];
108161
109- $ this ->unPatchFile ($ file , $ value );
162+ $ newContents = $ this ->getUnPatchedContents ($ file , $ value );
163+ $ this ->fileContents [$ file ] = $ newContents ;
110164 }
111165 }
112166
113- public function update ( RecipeUpdate $ recipeUpdate , array $ originalConfig , array $ newConfig ): void
167+ private function getPatchedContents ( string $ file , string $ value , string $ position , ? string $ target , bool $ warnIfMissing ): string
114168 {
115- $ originalConfig = array_filter ($ originalConfig , function ($ item ) {
116- return !isset ($ item ['requires ' ]) || $ this ->isPackageInstalled ($ item ['requires ' ]);
117- });
118- $ newConfig = array_filter ($ newConfig , function ($ item ) {
119- return !isset ($ item ['requires ' ]) || $ this ->isPackageInstalled ($ item ['requires ' ]);
120- });
121-
122- $ filterDuplicates = function (array $ sourceConfig , array $ comparisonConfig ) {
123- $ filtered = [];
124- foreach ($ sourceConfig as $ sourceItem ) {
125- $ found = false ;
126- foreach ($ comparisonConfig as $ comparisonItem ) {
127- if ($ sourceItem ['file ' ] === $ comparisonItem ['file ' ] && $ sourceItem ['content ' ] === $ comparisonItem ['content ' ]) {
128- $ found = true ;
129- break ;
130- }
131- }
132- if (!$ found ) {
133- $ filtered [] = $ sourceItem ;
134- }
135- }
136-
137- return $ filtered ;
138- };
139-
140- // remove any config where the file+value is the same before & after
141- $ filteredOriginalConfig = $ filterDuplicates ($ originalConfig , $ newConfig );
142- $ filteredNewConfig = $ filterDuplicates ($ newConfig , $ originalConfig );
143-
144- $ this ->unconfigure ($ recipeUpdate ->getOriginalRecipe (), $ filteredOriginalConfig , $ recipeUpdate ->getLock ());
145- $ this ->configure ($ recipeUpdate ->getNewRecipe (), $ filteredNewConfig , $ recipeUpdate ->getLock ());
146- }
147-
148- private function patchFile (string $ file , string $ value , string $ position , ?string $ target , bool $ warnIfMissing )
149- {
150- $ fileContents = file_get_contents ($ file );
169+ $ fileContents = $ this ->readFile ($ file );
151170
152171 if (false !== strpos ($ fileContents , $ value )) {
153- return ; // already includes value, skip
172+ return $ fileContents ; // already includes value, skip
154173 }
155174
156175 switch ($ position ) {
@@ -188,15 +207,15 @@ private function patchFile(string $file, string $value, string $position, ?strin
188207 break ;
189208 }
190209
191- file_put_contents ( $ file , $ fileContents) ;
210+ return $ fileContents ;
192211 }
193212
194- private function unPatchFile (string $ file , $ value )
213+ private function getUnPatchedContents (string $ file , $ value ): string
195214 {
196- $ fileContents = file_get_contents ($ file );
215+ $ fileContents = $ this -> readFile ($ file );
197216
198217 if (false === strpos ($ fileContents , $ value )) {
199- return ; // value already gone!
218+ return $ fileContents ; // value already gone!
200219 }
201220
202221 if (false !== strpos ($ fileContents , "\n" .$ value )) {
@@ -206,9 +225,8 @@ private function unPatchFile(string $file, $value)
206225 }
207226
208227 $ position = strpos ($ fileContents , $ value );
209- $ fileContents = substr_replace ($ fileContents , '' , $ position , \strlen ($ value ));
210228
211- file_put_contents ( $ file , $ fileContents );
229+ return substr_replace ( $ fileContents , '' , $ position , \strlen ( $ value ) );
212230 }
213231
214232 private function isPackageInstalled ($ packages ): bool
@@ -227,4 +245,26 @@ private function isPackageInstalled($packages): bool
227245
228246 return true ;
229247 }
248+
249+ private function relativize (string $ path ): string
250+ {
251+ $ rootDir = $ this ->options ->get ('root-dir ' );
252+ if (0 === strpos ($ path , $ rootDir )) {
253+ $ path = substr ($ path , \strlen ($ rootDir ) + 1 );
254+ }
255+
256+ return ltrim ($ path , '/ \\' );
257+ }
258+
259+ private function readFile (string $ file ): string
260+ {
261+ if (isset ($ this ->fileContents [$ file ])) {
262+ return $ this ->fileContents [$ file ];
263+ }
264+
265+ $ fileContents = file_get_contents ($ file );
266+ $ this ->fileContents [$ file ] = $ fileContents ;
267+
268+ return $ fileContents ;
269+ }
230270}
0 commit comments