55
66use PhpParser \Node ;
77use PhpParser \Node \Arg ;
8+ use PhpParser \Node \Expr ;
9+ use PhpParser \Node \Expr \BinaryOp \BitwiseOr ;
810use PhpParser \Node \Expr \ConstFetch ;
911use PhpParser \Node \Expr \FuncCall ;
1012use PhpParser \Node \Identifier ;
@@ -31,6 +33,10 @@ final class JsonThrowOnErrorRector extends AbstractRector implements MinPhpVersi
3133 * @readonly
3234 */
3335 private BetterNodeFinder $ betterNodeFinder ;
36+ /**
37+ * @var mixed[]
38+ */
39+ private const FLAGS = ['JSON_THROW_ON_ERROR ' ];
3440 private bool $ hasChanged = \false;
3541 public function __construct (ValueResolver $ valueResolver , BetterNodeFinder $ betterNodeFinder )
3642 {
@@ -56,9 +62,6 @@ public function getNodeTypes(): array
5662 {
5763 return NodeGroup::STMTS_AWARE ;
5864 }
59- /**
60- * @param StmtsAware $node
61- */
6265 public function refactor (Node $ node ): ?Node
6366 {
6467 // if found, skip it :)
@@ -109,19 +112,28 @@ private function shouldSkipFuncCall(FuncCall $funcCall): bool
109112 }
110113 return $ this ->isFirstValueStringOrArray ($ funcCall );
111114 }
112- private function processJsonEncode (FuncCall $ funcCall ): ? FuncCall
115+ private function processJsonEncode (FuncCall $ funcCall ): FuncCall
113116 {
117+ $ flags = [];
114118 if (isset ($ funcCall ->args [1 ])) {
115- return null ;
119+ /** @var Arg $arg */
120+ $ arg = $ funcCall ->args [1 ];
121+ $ flags = $ this ->getFlags ($ arg );
122+ }
123+ $ newArg = $ this ->getArgWithFlags ($ flags );
124+ if ($ newArg instanceof Arg) {
125+ $ this ->hasChanged = \true;
126+ $ funcCall ->args [1 ] = $ newArg ;
116127 }
117- $ this ->hasChanged = \true;
118- $ funcCall ->args [1 ] = new Arg ($ this ->createConstFetch ('JSON_THROW_ON_ERROR ' ));
119128 return $ funcCall ;
120129 }
121- private function processJsonDecode (FuncCall $ funcCall ): ? FuncCall
130+ private function processJsonDecode (FuncCall $ funcCall ): FuncCall
122131 {
132+ $ flags = [];
123133 if (isset ($ funcCall ->args [3 ])) {
124- return null ;
134+ /** @var Arg $arg */
135+ $ arg = $ funcCall ->args [3 ];
136+ $ flags = $ this ->getFlags ($ arg );
125137 }
126138 // set default to inter-args
127139 if (!isset ($ funcCall ->args [1 ])) {
@@ -130,8 +142,11 @@ private function processJsonDecode(FuncCall $funcCall): ?FuncCall
130142 if (!isset ($ funcCall ->args [2 ])) {
131143 $ funcCall ->args [2 ] = new Arg (new Int_ (512 ));
132144 }
133- $ this ->hasChanged = \true;
134- $ funcCall ->args [3 ] = new Arg ($ this ->createConstFetch ('JSON_THROW_ON_ERROR ' ));
145+ $ newArg = $ this ->getArgWithFlags ($ flags );
146+ if ($ newArg instanceof Arg) {
147+ $ this ->hasChanged = \true;
148+ $ funcCall ->args [3 ] = $ newArg ;
149+ }
135150 return $ funcCall ;
136151 }
137152 private function createConstFetch (string $ name ): ConstFetch
@@ -150,4 +165,49 @@ private function isFirstValueStringOrArray(FuncCall $funcCall): bool
150165 }
151166 return is_array ($ value );
152167 }
168+ /**
169+ * @param string[] $flags
170+ * @return string[]
171+ * @param \PhpParser\Node\Expr|\PhpParser\Node\Arg $arg
172+ */
173+ private function getFlags ($ arg , array $ flags = []): array
174+ {
175+ // Unwrap Arg
176+ if ($ arg instanceof Arg) {
177+ $ arg = $ arg ->value ;
178+ }
179+ // Single flag: SOME_CONST
180+ if ($ arg instanceof ConstFetch) {
181+ $ flags [] = $ arg ->name ->getFirst ();
182+ return $ flags ;
183+ }
184+ // Multiple flags: FLAG_A | FLAG_B | FLAG_C
185+ if ($ arg instanceof BitwiseOr) {
186+ $ flags = $ this ->getFlags ($ arg ->left , $ flags );
187+ $ flags = $ this ->getFlags ($ arg ->right , $ flags );
188+ }
189+ return array_values (array_unique ($ flags ));
190+ // array_unique in case the same flag is written multiple times
191+ }
192+ /**
193+ * @param string[] $flags
194+ */
195+ private function getArgWithFlags (array $ flags ): ?Arg
196+ {
197+ $ originalCount = count ($ flags );
198+ $ flags = array_values (array_unique (array_merge ($ flags , self ::FLAGS )));
199+ if ($ originalCount === count ($ flags )) {
200+ return null ;
201+ }
202+ // Single flag
203+ if (count ($ flags ) === 1 ) {
204+ return new Arg ($ this ->createConstFetch ($ flags [0 ]));
205+ }
206+ // Build FLAG_A | FLAG_B | FLAG_C
207+ $ expr = $ this ->createConstFetch (array_shift ($ flags ));
208+ foreach ($ flags as $ flag ) {
209+ $ expr = new BitwiseOr ($ expr , $ this ->createConstFetch ($ flag ));
210+ }
211+ return new Arg ($ expr );
212+ }
153213}
0 commit comments