55
66use PhpParser \Node ;
77use PhpParser \Node \Arg ;
8+ use PhpParser \Node \Expr ;
89use PhpParser \Node \Expr \ConstFetch ;
910use PhpParser \Node \Expr \FuncCall ;
1011use PhpParser \Node \Identifier ;
@@ -32,6 +33,10 @@ final class JsonThrowOnErrorRector extends AbstractRector implements MinPhpVersi
3233 */
3334 private BetterNodeFinder $ betterNodeFinder ;
3435 private bool $ hasChanged = \false;
36+ /**
37+ * @var mixed[]
38+ */
39+ private const FLAGS = ['JSON_THROW_ON_ERROR ' ];
3540 public function __construct (ValueResolver $ valueResolver , BetterNodeFinder $ betterNodeFinder )
3641 {
3742 $ this ->valueResolver = $ valueResolver ;
@@ -56,9 +61,6 @@ public function getNodeTypes(): array
5661 {
5762 return NodeGroup::STMTS_AWARE ;
5863 }
59- /**
60- * @param StmtsAware $node
61- */
6264 public function refactor (Node $ node ): ?Node
6365 {
6466 // if found, skip it :)
@@ -109,19 +111,28 @@ private function shouldSkipFuncCall(FuncCall $funcCall): bool
109111 }
110112 return $ this ->isFirstValueStringOrArray ($ funcCall );
111113 }
112- private function processJsonEncode (FuncCall $ funcCall ): ? FuncCall
114+ private function processJsonEncode (FuncCall $ funcCall ): FuncCall
113115 {
116+ $ flags = [];
114117 if (isset ($ funcCall ->args [1 ])) {
115- return null ;
118+ /** @var Arg $arg */
119+ $ arg = $ funcCall ->args [1 ];
120+ $ flags = $ this ->getFlags ($ arg );
121+ }
122+ $ newArg = $ this ->getArgWithFlags ($ flags );
123+ if ($ newArg instanceof Arg) {
124+ $ this ->hasChanged = \true;
125+ $ funcCall ->args [1 ] = $ newArg ;
116126 }
117- $ this ->hasChanged = \true;
118- $ funcCall ->args [1 ] = new Arg ($ this ->createConstFetch ('JSON_THROW_ON_ERROR ' ));
119127 return $ funcCall ;
120128 }
121- private function processJsonDecode (FuncCall $ funcCall ): ? FuncCall
129+ private function processJsonDecode (FuncCall $ funcCall ): FuncCall
122130 {
131+ $ flags = [];
123132 if (isset ($ funcCall ->args [3 ])) {
124- return null ;
133+ /** @var Arg $arg */
134+ $ arg = $ funcCall ->args [3 ];
135+ $ flags = $ this ->getFlags ($ arg );
125136 }
126137 // set default to inter-args
127138 if (!isset ($ funcCall ->args [1 ])) {
@@ -130,8 +141,11 @@ private function processJsonDecode(FuncCall $funcCall): ?FuncCall
130141 if (!isset ($ funcCall ->args [2 ])) {
131142 $ funcCall ->args [2 ] = new Arg (new Int_ (512 ));
132143 }
133- $ this ->hasChanged = \true;
134- $ funcCall ->args [3 ] = new Arg ($ this ->createConstFetch ('JSON_THROW_ON_ERROR ' ));
144+ $ newArg = $ this ->getArgWithFlags ($ flags );
145+ if ($ newArg instanceof Arg) {
146+ $ this ->hasChanged = \true;
147+ $ funcCall ->args [3 ] = $ newArg ;
148+ }
135149 return $ funcCall ;
136150 }
137151 private function createConstFetch (string $ name ): ConstFetch
@@ -150,4 +164,49 @@ private function isFirstValueStringOrArray(FuncCall $funcCall): bool
150164 }
151165 return is_array ($ value );
152166 }
167+ /**
168+ * @param string[] $flags
169+ * @return string[]
170+ * @param \PhpParser\Node\Expr|\PhpParser\Node\Arg $arg
171+ */
172+ private function getFlags ($ arg , array $ flags = []): array
173+ {
174+ // Unwrap Arg
175+ if ($ arg instanceof Arg) {
176+ $ arg = $ arg ->value ;
177+ }
178+ // Single flag: SOME_CONST
179+ if ($ arg instanceof ConstFetch) {
180+ $ flags [] = $ arg ->name ->getFirst ();
181+ return $ flags ;
182+ }
183+ // Multiple flags: FLAG_A | FLAG_B | FLAG_C
184+ if ($ arg instanceof Node \Expr \BinaryOp \BitwiseOr) {
185+ $ flags = $ this ->getFlags ($ arg ->left , $ flags );
186+ $ flags = $ this ->getFlags ($ arg ->right , $ flags );
187+ }
188+ return array_values (array_unique ($ flags ));
189+ // array_unique in case the same flag is written multiple times
190+ }
191+ /**
192+ * @param string[] $flags
193+ */
194+ private function getArgWithFlags (array $ flags ): ?Arg
195+ {
196+ $ originalCount = count ($ flags );
197+ $ flags = array_values (array_unique (array_merge ($ flags , self ::FLAGS )));
198+ if ($ originalCount === count ($ flags )) {
199+ return null ;
200+ }
201+ // Single flag
202+ if (count ($ flags ) === 1 ) {
203+ return new Arg ($ this ->createConstFetch ($ flags [0 ]));
204+ }
205+ // Build FLAG_A | FLAG_B | FLAG_C
206+ $ expr = $ this ->createConstFetch (array_shift ($ flags ));
207+ foreach ($ flags as $ flag ) {
208+ $ expr = new Node \Expr \BinaryOp \BitwiseOr ($ expr , $ this ->createConstFetch ($ flag ));
209+ }
210+ return new Arg ($ expr );
211+ }
153212}
0 commit comments