]> git.mxchange.org Git - friendica.git/blob - src/Util/JsonLD.php
Merge pull request #8079 from ozero/patch-1
[friendica.git] / src / Util / JsonLD.php
1 <?php
2 /**
3  * @file src/Util/JsonLD.php
4  */
5 namespace Friendica\Util;
6
7 use Friendica\Core\Cache\Cache;
8 use Friendica\Core\Logger;
9 use Exception;
10 use Friendica\DI;
11
12 /**
13  * @brief This class contain methods to work with JsonLD data
14  */
15 class JsonLD
16 {
17         /**
18          * @brief Loader for LD-JSON validation
19          *
20          * @param $url
21          *
22          * @return mixed the loaded data
23          * @throws \JsonLdException
24          */
25         public static function documentLoader($url)
26         {
27                 $recursion = 0;
28
29                 $x = debug_backtrace();
30                 if ($x) {
31                         foreach ($x as $n) {
32                                 if ($n['function'] === __FUNCTION__)  {
33                                         $recursion ++;
34                                 }
35                         }
36                 }
37
38                 if ($recursion > 5) {
39                         Logger::error('jsonld bomb detected at: ' . $url);
40                         exit();
41                 }
42
43                 $result = DI::cache()->get('documentLoader:' . $url);
44                 if (!is_null($result)) {
45                         return $result;
46                 }
47
48                 $data = jsonld_default_document_loader($url);
49                 DI::cache()->set('documentLoader:' . $url, $data, Cache::DAY);
50                 return $data;
51         }
52
53         /**
54          * @brief Normalises a given JSON array
55          *
56          * @param array $json
57          *
58          * @return mixed|bool normalized JSON string
59          * @throws Exception
60          */
61         public static function normalize($json)
62         {
63                 jsonld_set_document_loader('Friendica\Util\JsonLD::documentLoader');
64
65                 $jsonobj = json_decode(json_encode($json, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
66
67                 try {
68                         $normalized = jsonld_normalize($jsonobj, array('algorithm' => 'URDNA2015', 'format' => 'application/nquads'));
69                 }
70                 catch (Exception $e) {
71                         $normalized = false;
72                         $messages = [];
73                         $currentException = $e;
74                         do {
75                                 $messages[] = $currentException->getMessage();
76                         } while($currentException = $currentException->getPrevious());
77
78                         Logger::warning('JsonLD normalize error');
79                         Logger::notice('JsonLD normalize error', ['messages' => $messages]);
80                         Logger::info('JsonLD normalize error', ['trace' => $e->getTraceAsString()]);
81                         Logger::debug('JsonLD normalize error', ['jsonobj' => $jsonobj]);
82                 }
83
84                 return $normalized;
85         }
86
87         /**
88          * @brief Compacts a given JSON array
89          *
90          * @param array $json
91          *
92          * @return array Compacted JSON array
93          * @throws Exception
94          */
95         public static function compact($json)
96         {
97                 jsonld_set_document_loader('Friendica\Util\JsonLD::documentLoader');
98
99                 $context = (object)['as' => 'https://www.w3.org/ns/activitystreams#',
100                         'w3id' => 'https://w3id.org/security#',
101                         'ldp' => (object)['@id' => 'http://www.w3.org/ns/ldp#', '@type' => '@id'],
102                         'vcard' => (object)['@id' => 'http://www.w3.org/2006/vcard/ns#', '@type' => '@id'],
103                         'dfrn' => (object)['@id' => 'http://purl.org/macgirvin/dfrn/1.0/', '@type' => '@id'],
104                         'diaspora' => (object)['@id' => 'https://diasporafoundation.org/ns/', '@type' => '@id'],
105                         'ostatus' => (object)['@id' => 'http://ostatus.org#', '@type' => '@id'],
106                         'dc' => (object)['@id' => 'http://purl.org/dc/terms/', '@type' => '@id'],
107                         'toot' => (object)['@id' => 'http://joinmastodon.org/ns#', '@type' => '@id'],
108                         'litepub' => (object)['@id' => 'http://litepub.social/ns#', '@type' => '@id']];
109
110                 // Preparation for adding possibly missing content to the context
111                 if (!empty($json['@context']) && is_string($json['@context'])) {
112                         $json['@context'] = [$json['@context']];
113                 }
114
115                 // Workaround for servers with missing context
116                 // See issue https://github.com/nextcloud/social/issues/330
117                 if (!empty($json['@context']) && is_array($json['@context'])) {
118                         $json['@context'][] = 'https://w3id.org/security/v1';
119                 }
120
121                 // Trying to avoid memory problems with large content fields
122                 if (!empty($json['object']['source']['content'])) {
123                         $content = $json['object']['source']['content'];
124                         $json['object']['source']['content'] = '';
125                 }
126
127                 $jsonobj = json_decode(json_encode($json, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
128
129                 try {
130                         $compacted = jsonld_compact($jsonobj, $context);
131                 }
132                 catch (Exception $e) {
133                         $compacted = false;
134                         Logger::error('compacting error');
135                         // Sooner or later we should log some details as well - but currently this leads to memory issues
136                         // Logger::log('compacting error:' . substr(print_r($e, true), 0, 10000), Logger::DEBUG);
137                 }
138
139                 $json = json_decode(json_encode($compacted, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), true);
140
141                 if (isset($json['as:object']['as:source']['as:content']) && !empty($content)) {
142                         $json['as:object']['as:source']['as:content'] = $content;
143                 }
144
145                 return $json;
146         }
147
148         /**
149          * @brief Fetches an element array from a JSON array
150          *
151          * @param $array
152          * @param $element
153          * @param $key
154          *
155          * @return array fetched element
156          */
157         public static function fetchElementArray($array, $element, $key = '@id')
158         {
159                 if (empty($array)) {
160                         return null;
161                 }
162
163                 if (!isset($array[$element])) {
164                         return null;
165                 }
166
167                 // If it isn't an array yet, make it to one
168                 if (!is_int(key($array[$element]))) {
169                         $array[$element] = [$array[$element]];
170                 }
171
172                 $elements = [];
173
174                 foreach ($array[$element] as $entry) {
175                         if (!is_array($entry)) {
176                                 $elements[] = $entry;
177                         } elseif (isset($entry[$key])) {
178                                 $elements[] = $entry[$key];
179                         } elseif (!empty($entry) || !is_array($entry)) {
180                                 $elements[] = $entry;
181                         }
182                 }
183
184                 return $elements;
185         }
186
187         /**
188          * @brief Fetches an element from a JSON array
189          *
190          * @param $array
191          * @param $element
192          * @param $key
193          * @param $type
194          * @param $type_value
195          *
196          * @return string fetched element
197          */
198         public static function fetchElement($array, $element, $key = '@id', $type = null, $type_value = null)
199         {
200                 if (empty($array)) {
201                         return null;
202                 }
203
204                 if (!isset($array[$element])) {
205                         return null;
206                 }
207
208                 if (!is_array($array[$element])) {
209                         return $array[$element];
210                 }
211
212                 if (is_null($type) || is_null($type_value)) {
213                         $element_array = self::fetchElementArray($array, $element, $key);
214                         if (is_null($element_array)) {
215                                 return null;
216                         }
217
218                         return array_shift($element_array);
219                 }
220
221                 $element_array = self::fetchElementArray($array, $element);
222                 if (is_null($element_array)) {
223                         return null;
224                 }
225
226                 foreach ($element_array as $entry) {
227                         if (isset($entry[$key]) && isset($entry[$type]) && ($entry[$type] == $type_value)) {
228                                 return $entry[$key];
229                         }
230                 }
231
232                 return null;
233         }
234 }