1717 */
1818final class Diff
1919{
20+ /**
21+ * @var array cached properties and their default values
22+ */
23+ private const CACHED_PROPERTIES = [
24+ 'groupedCodes ' => null ,
25+ ];
26+
2027 /**
2128 * @var array array of the options that have been applied for generating the diff
2229 */
@@ -32,6 +39,11 @@ final class Diff
3239 */
3340 private $ new = [];
3441
42+ /**
43+ * @var bool is any of cached properties dirty?
44+ */
45+ private $ isCacheDirty = true ;
46+
3547 /**
3648 * @var null|SequenceMatcher the sequence matcher
3749 */
@@ -94,8 +106,7 @@ public function setOld(array $old): self
94106 {
95107 if ($ this ->old !== $ old ) {
96108 $ this ->old = $ old ;
97- $ this ->sequenceMatcher ->setSeq1 ($ old );
98- $ this ->resetCachedResults ();
109+ $ this ->isCacheDirty = true ;
99110 }
100111
101112 return $ this ;
@@ -112,8 +123,7 @@ public function setNew(array $new): self
112123 {
113124 if ($ this ->new !== $ new ) {
114125 $ this ->new = $ new ;
115- $ this ->sequenceMatcher ->setSeq2 ($ new );
116- $ this ->resetCachedResults ();
126+ $ this ->isCacheDirty = true ;
117127 }
118128
119129 return $ this ;
@@ -128,10 +138,12 @@ public function setNew(array $new): self
128138 */
129139 public function setOptions (array $ options ): self
130140 {
131- $ this -> options = $ options + static ::$ defaultOptions ;
141+ $ mergedOptions = $ options + static ::$ defaultOptions ;
132142
133- $ this ->sequenceMatcher ->setOptions ($ this ->options );
134- $ this ->resetCachedResults ();
143+ if ($ this ->options !== $ mergedOptions ) {
144+ $ this ->options = $ mergedOptions ;
145+ $ this ->isCacheDirty = true ;
146+ }
135147
136148 return $ this ;
137149 }
@@ -198,22 +210,12 @@ public static function getInstance(): self
198210 */
199211 public function getGroupedOpcodes (): array
200212 {
213+ $ this ->finalize ();
214+
201215 return $ this ->groupedCodes = $ this ->groupedCodes ??
202216 $ this ->sequenceMatcher ->getGroupedOpcodes ($ this ->options ['context ' ]);
203217 }
204218
205- /**
206- * Reset cached results.
207- *
208- * @return self
209- */
210- public function resetCachedResults (): self
211- {
212- $ this ->groupedCodes = null ;
213-
214- return $ this ;
215- }
216-
217219 /**
218220 * Render a diff using the supplied rendering class and return it.
219221 *
@@ -223,6 +225,8 @@ public function resetCachedResults(): self
223225 */
224226 public function render (AbstractRenderer $ renderer ): string
225227 {
228+ $ this ->finalize ();
229+
226230 $ renderer ->setDiff ($ this );
227231
228232 // the "no difference" situation may happen frequently
@@ -232,6 +236,43 @@ public function render(AbstractRenderer $renderer): string
232236 : $ renderer ->render ();
233237 }
234238
239+ /**
240+ * Claim this class is all set.
241+ *
242+ * Properties will be propagated to other classes. You must re-call
243+ * this method after any property changed before doing calculation.
244+ *
245+ * @return self
246+ */
247+ private function finalize (): self
248+ {
249+ if ($ this ->isCacheDirty ) {
250+ $ this ->resetCachedResults ();
251+
252+ $ this ->sequenceMatcher
253+ ->setOptions ($ this ->options )
254+ ->setSequences ($ this ->old , $ this ->new );
255+ }
256+
257+ return $ this ;
258+ }
259+
260+ /**
261+ * Reset cached results.
262+ *
263+ * @return self
264+ */
265+ private function resetCachedResults (): self
266+ {
267+ foreach (static ::CACHED_PROPERTIES as $ property => $ value ) {
268+ $ this ->$ property = $ value ;
269+ }
270+
271+ $ this ->isCacheDirty = false ;
272+
273+ return $ this ;
274+ }
275+
235276 /**
236277 * The work horse of getA() and getB().
237278 *
0 commit comments