Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
JsonCodecableWithCodecTrait
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 4
56
0.00% covered (danger)
0.00%
0 / 1
 jsonClassCodec
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 toJsonArray
n/a
0 / 0
n/a
0 / 0
0
 newFromJsonArray
n/a
0 / 0
n/a
0 / 0
0
 jsonClassHintFor
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 decodeDocumentFragment
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 encodeDocumentFragment
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2declare( strict_types=1 );
3
4/**
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 */
22namespace Wikimedia\Parsoid\Utils;
23
24use Psr\Container\ContainerInterface;
25use stdClass;
26use Wikimedia\JsonCodec\Hint;
27use Wikimedia\JsonCodec\JsonClassCodec;
28use Wikimedia\JsonCodec\JsonCodecInterface;
29use Wikimedia\Parsoid\DOM\DocumentFragment;
30
31/**
32 * This is an implementation of JsonCodecableTrait which passes the
33 * JsonCodec along to the class.  This allows us to implement
34 * custom encodings for properties with DocumentFragment types
35 * by manually encoding the property.
36 */
37trait JsonCodecableWithCodecTrait {
38    /**
39     * Implements JsonCodecable by providing an implementation of
40     * ::jsonClassCodec() which does not use the provided $serviceContainer
41     * nor does it maintain any state; it just calls the ::toJsonArray()
42     * and ::newFromJsonArray() methods of this instance, passing along
43     * the JsonCodecInterface to allow custom encodings.
44     * @param JsonCodecInterface $codec
45     * @param ContainerInterface $serviceContainer
46     * @return JsonClassCodec
47     */
48    public static function jsonClassCodec(
49        JsonCodecInterface $codec, ContainerInterface $serviceContainer
50    ): JsonClassCodec {
51        /** @implements JsonClassCodec<JsonCodecableWithCodecTrait> */
52        return new class( $codec ) implements JsonClassCodec {
53
54            public function __construct( private JsonCodecInterface $codec ) {
55            }
56
57            /** @inheritDoc */
58            public function toJsonArray( $obj ): array {
59                return $obj->toJsonArray( $this->codec );
60            }
61
62            /** @inheritDoc */
63            public function newFromJsonArray( string $className, array $json ) {
64                return $className::newFromJsonArray( $this->codec, $json );
65            }
66
67            /** @inheritDoc */
68            public function jsonClassHintFor( string $className, string $keyName ) {
69                return $className::jsonClassHintFor( $keyName );
70            }
71        };
72    }
73
74    /**
75     * Return an associative array representing the contents of this object,
76     * which can be passed to ::newFromJsonArray() to deserialize it.
77     * @param JsonCodecInterface $codec For custom encodings
78     * @return array
79     */
80    abstract public function toJsonArray( JsonCodecInterface $codec ): array;
81
82    /**
83     * Return an instance of this object representing the deserialization
84     * from the array passed in $json.
85     * @param JsonCodecInterface $codec For custom encodings
86     * @param array $json
87     * @return stdClass
88     */
89    abstract public static function newFromJsonArray( JsonCodecInterface $codec, array $json );
90
91    /**
92     * Return an optional type hint for the given array key in the result of
93     * ::toJsonArray() / input to ::newFromJsonArray.  If a class name is
94     * returned here and it matches the runtime type of the value of that
95     * array key, then type information will be omitted from the generated
96     * JSON which can save space.  The class name can be suffixed with `[]`
97     * to indicate an array or list containing objects of the given class
98     * name.
99     *
100     * Default implementation of ::jsonClassHintFor() provides no hints.
101     * Implementer can override.
102     *
103     * @param string $keyName
104     * @return class-string|string|Hint|null A class string, Hint, or null.
105     *   For backward compatibility, a class string suffixed with `[]` can
106     *   also be returned, but that is deprecated.
107     */
108    public static function jsonClassHintFor( string $keyName ) {
109        return null;
110    }
111
112    // Helper methods specific to Parsoid.
113
114    /**
115     * Helper function for deserializing DocumentFragments which
116     * could be encoded as strings.
117     * @param JsonCodecInterface $codec
118     * @param string|array|DocumentFragment $v
119     * @return DocumentFragment
120     */
121    private static function decodeDocumentFragment( JsonCodecInterface $codec, $v ): DocumentFragment {
122        // Usually '_h' or '_t' is used as a marker for caption/html, but
123        // allow a bare string as well.
124        // If v is a string, rewrite it to match the 'expected'
125        // [ _h => '...' ] serialization of a DocumentFragment.
126        $v = is_string( $v ) ? [ '_h' => $v ] : $v;
127        if ( is_array( $v ) ) {
128            $v = $codec->newFromJsonArray( $v, DocumentFragment::class );
129        }
130        return $v;
131    }
132
133    /**
134     * Helper function for serializing DocumentFragments as strings,
135     * for compatibility w/ existing MediaWiki DOM Spec 2.8.0.
136     * @param JsonCodecInterface $codec
137     * @param DocumentFragment $df
138     * @return string|array
139     */
140    private static function encodeDocumentFragment( JsonCodecInterface $codec, DocumentFragment $df ) {
141        // compatibility with MediaWiki DOM Spec 2.8.0
142        // See [[mw:Parsoid/MediaWiki DOM spec/Rich Attributes]] Phase 3
143        // for discussion about alternate _h/_t marking for DocumentFragments
144        $c = $codec->toJsonArray( $df, DocumentFragment::class );
145        if ( is_string( $c['_h'] ?? null ) ) {
146            return $c['_h'];
147        }
148        return $c;
149    }
150}