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
@@ -311,6 +315,43 @@ public function getB(int $start = 0, ?int $end = null): array
311315 return $ this ->getNew ($ start , $ end );
312316 }
313317
318+ /**
319+ * Claim this class is all set.
320+ *
321+ * Properties will be propagated to other classes. You must re-call
322+ * this method after any property changed before doing calculation.
323+ *
324+ * @return self
325+ */
326+ private function finalize (): self
327+ {
328+ if ($ this ->isCacheDirty ) {
329+ $ this ->resetCachedResults ();
330+
331+ $ this ->sequenceMatcher
332+ ->setOptions ($ this ->options )
333+ ->setSequences ($ this ->old , $ this ->new );
334+ }
335+
336+ return $ this ;
337+ }
338+
339+ /**
340+ * Reset cached results.
341+ *
342+ * @return self
343+ */
344+ private function resetCachedResults (): self
345+ {
346+ foreach (static ::CACHED_PROPERTIES as $ property => $ value ) {
347+ $ this ->$ property = $ value ;
348+ }
349+
350+ $ this ->isCacheDirty = false ;
351+
352+ return $ this ;
353+ }
354+
314355 /**
315356 * The work horse of getA() and getB().
316357 *
0 commit comments