Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

ElementInspector.php

Zuletzt modifiziert: 02.04.2025, 15:04 - Dateigröße: 15.52 KiB


001  <?php
002   
003  /**
004  * @package   s9e\TextFormatter
005  * @copyright Copyright (c) 2010-2022 The s9e authors
006  * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
007  */
008  namespace s9e\TextFormatter\Configurator\Helpers;
009   
010  use DOMElement;
011  use DOMXPath;
012   
013  class ElementInspector
014  {
015      /**
016      * This is an abridged version of the HTML5 content models and rules, with some liberties taken.
017      *
018      * For each element, up to three bitfields are defined: "c", "ac" and "dd". Bitfields are stored
019      * as raw bytes, formatted using the octal notation to keep the sources ASCII.
020      *
021      *    "c" represents the categories the element belongs to. The categories are comprised of HTML5
022      *        content models (such as "phrasing content" or "interactive content") plus a few special
023      *        categories created to cover the parts of the specs that refer to "a group of X and Y
024      *        elements" rather than a specific content model.
025      *
026      *   "ac" represents the categories that are allowed as children of given element.
027      *
028      *   "dd" represents the categories that must not appear as a descendant of given element.
029      *
030      * Sometimes, HTML5 specifies some restrictions on when an element can accept certain children,
031      * or what categories the element belongs to. For example, an <img> element is only part of the
032      * "interactive content" category if it has a "usemap" attribute. Those restrictions are
033      * expressed as an XPath expression and stored using the concatenation of the key of the bitfield
034      * plus the bit number of the category. For instance, if "interactive content" got assigned to
035      * bit 2, the definition of the <img> element will contain a key "c2" with value "@usemap".
036      *
037      * Additionally, other flags are set:
038      *
039      *    "t" indicates that the element uses the "transparent" content model.
040      *    "e" indicates that the element uses the "empty" content model.
041      *    "v" indicates that the element is a void element.
042      *   "nt" indicates that the element does not accept text nodes. (no text)
043      *   "to" indicates that the element should only contain text. (text-only)
044      *   "fe" indicates that the element is a formatting element. It will automatically be reopened
045      *        when closed by an end tag of a different name.
046      *    "b" indicates that the element is not phrasing content, which makes it likely to act like
047      *        a block element.
048      *
049      * Finally, HTML5 defines "optional end tag" rules, where one element automatically closes its
050      * predecessor. Those are used to generate closeParent rules and are stored in the "cp" key.
051      *
052      * @var array
053      * @see /scripts/patchElementInspector.php
054      */
055      protected static $htmlElements = [
056          'a'=>['c'=>"\17\0\0\0\200",'c3'=>'@href','ac'=>"\0",'dd'=>"\10\0\0\0\200",'t'=>1,'fe'=>1],
057          'abbr'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
058          'address'=>['c'=>"\3\20",'ac'=>"\1",'dd'=>"\100\24",'b'=>1,'cp'=>['p']],
059          'area'=>['c'=>"\5",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1],
060          'article'=>['c'=>"\3\4",'ac'=>"\1",'dd'=>"\0",'b'=>1,'cp'=>['p']],
061          'aside'=>['c'=>"\3\4",'ac'=>"\1",'dd'=>"\0",'b'=>1,'cp'=>['p']],
062          'audio'=>['c'=>"\57\0\10",'c3'=>'@controls','c1'=>'@controls','ac'=>"\0\0\0\220",'ac28'=>'not(@src)','dd'=>"\0\0\10",'t'=>1],
063          'b'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0",'fe'=>1],
064          'base'=>['c'=>"\20",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1,'b'=>1],
065          'bdi'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
066          'bdo'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
067          'blockquote'=>['c'=>"\3",'ac'=>"\1",'dd'=>"\0",'b'=>1,'cp'=>['p']],
068          'body'=>['c'=>"\0\0\20",'ac'=>"\1",'dd'=>"\0",'b'=>1],
069          'br'=>['c'=>"\5",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1],
070          'button'=>['c'=>"\217",'ac'=>"\4",'dd'=>"\10"],
071          'canvas'=>['c'=>"\47",'ac'=>"\0",'dd'=>"\10",'t'=>1],
072          'caption'=>['c'=>"\0\2",'ac'=>"\1",'dd'=>"\0\0\0\0\1",'b'=>1],
073          'cite'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
074          'code'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0",'fe'=>1],
075          'col'=>['c'=>"\0\0\100",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1,'b'=>1],
076          'colgroup'=>['c'=>"\0\2",'ac'=>"\0\0\100",'ac22'=>'not(@span)','dd'=>"\0",'nt'=>1,'e'=>1,'e?'=>'@span','b'=>1],
077          'data'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
078          'datalist'=>['c'=>"\5",'ac'=>"\4\200\0\40",'dd'=>"\0"],
079          'dd'=>['c'=>"\0\100\0\1",'ac'=>"\1",'dd'=>"\0",'b'=>1,'cp'=>['dd','dt']],
080          'del'=>['c'=>"\5",'ac'=>"\0",'dd'=>"\0",'t'=>1],
081          'details'=>['c'=>"\13",'ac'=>"\1\0\0\10",'dd'=>"\0",'b'=>1,'cp'=>['p']],
082          'dfn'=>['c'=>"\7\0\0\0\20",'ac'=>"\4",'dd'=>"\0\0\0\0\20"],
083          'dialog'=>['c'=>"\1",'ac'=>"\1",'dd'=>"\0",'b'=>1],
084          'div'=>['c'=>"\3\100",'ac'=>"\1\200\0\1",'ac0'=>'not(ancestor::dl)','dd'=>"\0",'b'=>1,'cp'=>['p']],
085          'dl'=>['c'=>"\3",'c1'=>'dt and dd','ac'=>"\0\300",'dd'=>"\0",'nt'=>1,'b'=>1,'cp'=>['p']],
086          'dt'=>['c'=>"\0\100\0\1",'ac'=>"\1",'dd'=>"\100\4\4",'b'=>1,'cp'=>['dd','dt']],
087          'em'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0",'fe'=>1],
088          'embed'=>['c'=>"\57",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1],
089          'fieldset'=>['c'=>"\203",'ac'=>"\1\0\0\100",'dd'=>"\0",'b'=>1,'cp'=>['p']],
090          'figcaption'=>['c'=>"\0\0\0\0\0\1",'ac'=>"\1",'dd'=>"\0",'b'=>1,'cp'=>['p']],
091          'figure'=>['c'=>"\3",'ac'=>"\1\0\0\0\0\1",'dd'=>"\0",'b'=>1,'cp'=>['p']],
092          'footer'=>['c'=>"\3\20\4",'ac'=>"\1",'dd'=>"\0\0\4",'b'=>1,'cp'=>['p']],
093          'form'=>['c'=>"\3\0\0\0\10",'ac'=>"\1",'dd'=>"\0\0\0\0\10",'b'=>1,'cp'=>['p']],
094          'h1'=>['c'=>"\103\1",'ac'=>"\4",'dd'=>"\0",'b'=>1,'cp'=>['p']],
095          'h2'=>['c'=>"\103\1",'ac'=>"\4",'dd'=>"\0",'b'=>1,'cp'=>['p']],
096          'h3'=>['c'=>"\103\1",'ac'=>"\4",'dd'=>"\0",'b'=>1,'cp'=>['p']],
097          'h4'=>['c'=>"\103\1",'ac'=>"\4",'dd'=>"\0",'b'=>1,'cp'=>['p']],
098          'h5'=>['c'=>"\103\1",'ac'=>"\4",'dd'=>"\0",'b'=>1,'cp'=>['p']],
099          'h6'=>['c'=>"\103\1",'ac'=>"\4",'dd'=>"\0",'b'=>1,'cp'=>['p']],
100          'head'=>['c'=>"\0\0\20",'ac'=>"\20",'dd'=>"\0",'nt'=>1,'b'=>1],
101          'header'=>['c'=>"\3\20\4",'ac'=>"\1",'dd'=>"\0\0\4",'b'=>1,'cp'=>['p']],
102          'hgroup'=>['c'=>"\103",'ac'=>"\0\201",'dd'=>"\0",'nt'=>1,'b'=>1,'cp'=>['p']],
103          'hr'=>['c'=>"\1\40",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1,'b'=>1,'cp'=>['p']],
104          'html'=>['c'=>"\0",'ac'=>"\0\0\20",'dd'=>"\0",'nt'=>1,'b'=>1],
105          'i'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0",'fe'=>1],
106          'iframe'=>['c'=>"\57",'ac'=>"\4",'dd'=>"\0"],
107          'img'=>['c'=>"\257\0\40",'c3'=>'@usemap','ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1],
108          'input'=>['c'=>"\217",'c3'=>'@type!="hidden"','c7'=>'@type!="hidden" or @type="hidden"','c1'=>'@type!="hidden"','ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1],
109          'ins'=>['c'=>"\7",'ac'=>"\0",'dd'=>"\0",'t'=>1],
110          'kbd'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
111          'label'=>['c'=>"\17\0\0\0\4",'ac'=>"\4",'dd'=>"\0\0\1\0\4"],
112          'legend'=>['c'=>"\0\0\0\100",'ac'=>"\104",'dd'=>"\0",'b'=>1],
113          'li'=>['c'=>"\0\0\0\0\100",'ac'=>"\1",'dd'=>"\0",'b'=>1,'cp'=>['li']],
114          'link'=>['c'=>"\25",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1],
115          'main'=>['c'=>"\3",'ac'=>"\1",'dd'=>"\0",'b'=>1,'cp'=>['p']],
116          'map'=>['c'=>"\7",'ac'=>"\0",'dd'=>"\0",'t'=>1],
117          'mark'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
118          'menu'=>['c'=>"\3\40",'c1'=>'li','ac'=>"\0\240",'dd'=>"\0",'nt'=>1,'b'=>1,'cp'=>['p']],
119          'menuitem'=>['c'=>"\0\40",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'b'=>1],
120          'meta'=>['c'=>"\25",'c0'=>'@itemprop','c2'=>'@itemprop','ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1],
121          'meter'=>['c'=>"\7\0\1\0\2",'ac'=>"\4",'dd'=>"\0\0\0\0\2"],
122          'nav'=>['c'=>"\3\4",'ac'=>"\1",'dd'=>"\0",'b'=>1,'cp'=>['p']],
123          'noscript'=>['c'=>"\25",'ac'=>"\0",'dd'=>"\0",'nt'=>1],
124          'object'=>['c'=>"\47\0\0\2",'ac'=>"\0",'dd'=>"\0",'t'=>1],
125          'ol'=>['c'=>"\3",'c1'=>'li','ac'=>"\0\200\0\0\100",'dd'=>"\0",'nt'=>1,'b'=>1,'cp'=>['p']],
126          'optgroup'=>['c'=>"\0\0\2",'ac'=>"\0\200\0\40",'dd'=>"\0",'nt'=>1,'b'=>1,'cp'=>['optgroup','option']],
127          'option'=>['c'=>"\0\0\2\40",'ac'=>"\0",'dd'=>"\0",'b'=>1,'cp'=>['option']],
128          'output'=>['c'=>"\207",'ac'=>"\4",'dd'=>"\0"],
129          'p'=>['c'=>"\3\1",'ac'=>"\4",'dd'=>"\0",'b'=>1,'cp'=>['p']],
130          'picture'=>['c'=>"\45",'ac'=>"\0\200\40",'dd'=>"\0",'nt'=>1],
131          'pre'=>['c'=>"\3",'ac'=>"\4",'dd'=>"\0",'pre'=>1,'b'=>1,'cp'=>['p']],
132          'progress'=>['c'=>"\7\0\1\4",'ac'=>"\4",'dd'=>"\0\0\0\4"],
133          'q'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
134          'rb'=>['c'=>"\0\10",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'b'=>1],
135          'rp'=>['c'=>"\0\10",'ac'=>"\0",'dd'=>"\0",'to'=>1,'b'=>1,'cp'=>['rp','rt']],
136          'rt'=>['c'=>"\0\10",'ac'=>"\4",'dd'=>"\0",'b'=>1,'cp'=>['rp','rt']],
137          'rtc'=>['c'=>"\0\10",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'b'=>1],
138          'ruby'=>['c'=>"\7",'ac'=>"\4\10",'dd'=>"\0"],
139          's'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0",'fe'=>1],
140          'samp'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
141          'script'=>['c'=>"\25\200",'ac'=>"\0",'dd'=>"\0",'to'=>1],
142          'section'=>['c'=>"\3\4",'ac'=>"\1",'dd'=>"\0",'b'=>1,'cp'=>['p']],
143          'select'=>['c'=>"\217",'ac'=>"\0\200\2",'dd'=>"\0",'nt'=>1],
144          'slot'=>['c'=>"\5",'ac'=>"\0",'dd'=>"\0",'t'=>1],
145          'small'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0",'fe'=>1],
146          'source'=>['c'=>"\0\0\40\20",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1,'b'=>1],
147          'span'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
148          'strong'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0",'fe'=>1],
149          'style'=>['c'=>"\20",'ac'=>"\0",'dd'=>"\0",'to'=>1,'b'=>1],
150          'sub'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
151          'summary'=>['c'=>"\0\0\0\10",'ac'=>"\104",'dd'=>"\0",'b'=>1],
152          'sup'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
153          'table'=>['c'=>"\3\0\0\0\1",'ac'=>"\0\202",'dd'=>"\0",'nt'=>1,'b'=>1,'cp'=>['p']],
154          'tbody'=>['c'=>"\0\2",'ac'=>"\0\200\0\0\40",'dd'=>"\0",'nt'=>1,'b'=>1,'cp'=>['tbody','td','th','thead','tr']],
155          'td'=>['c'=>"\0\0\200",'ac'=>"\1",'dd'=>"\0",'b'=>1,'cp'=>['td','th']],
156          'template'=>['c'=>"\25\200\100",'ac'=>"\0",'dd'=>"\0",'nt'=>1],
157          'textarea'=>['c'=>"\217",'ac'=>"\0",'dd'=>"\0",'pre'=>1,'to'=>1],
158          'tfoot'=>['c'=>"\0\2",'ac'=>"\0\200\0\0\40",'dd'=>"\0",'nt'=>1,'b'=>1,'cp'=>['tbody','td','th','thead','tr']],
159          'th'=>['c'=>"\0\0\200",'ac'=>"\1",'dd'=>"\100\4\4",'b'=>1,'cp'=>['td','th']],
160          'thead'=>['c'=>"\0\2",'ac'=>"\0\200\0\0\40",'dd'=>"\0",'nt'=>1,'b'=>1],
161          'time'=>['c'=>"\7",'ac'=>"\4",'ac2'=>'@datetime','dd'=>"\0"],
162          'title'=>['c'=>"\20",'ac'=>"\0",'dd'=>"\0",'to'=>1,'b'=>1],
163          'tr'=>['c'=>"\0\2\0\0\40",'ac'=>"\0\200\200",'dd'=>"\0",'nt'=>1,'b'=>1,'cp'=>['td','th','tr']],
164          'track'=>['c'=>"\0\0\0\200",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1,'b'=>1],
165          'u'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0",'fe'=>1],
166          'ul'=>['c'=>"\3",'c1'=>'li','ac'=>"\0\200\0\0\100",'dd'=>"\0",'nt'=>1,'b'=>1,'cp'=>['p']],
167          'var'=>['c'=>"\7",'ac'=>"\4",'dd'=>"\0"],
168          'video'=>['c'=>"\57\0\10",'c3'=>'@controls','ac'=>"\0\0\0\220",'ac28'=>'not(@src)','dd'=>"\0\0\10",'t'=>1],
169          'wbr'=>['c'=>"\5",'ac'=>"\0",'dd'=>"\0",'nt'=>1,'e'=>1,'v'=>1]
170      ];
171   
172      /**
173      * Test whether given child element closes given parent element
174      *
175      * @param  DOMElement $child
176      * @param  DOMElement $parent
177      * @return bool
178      */
179      public static function closesParent(DOMElement $child, DOMElement $parent)
180      {
181          $parentName = $parent->nodeName;
182          $childName  = $child->nodeName;
183   
184          return !empty(self::$htmlElements[$childName]['cp']) && in_array($parentName, self::$htmlElements[$childName]['cp'], true);
185      }
186   
187      /**
188      * Test whether given element disallows text nodes
189      *
190      * @param  DOMElement $element
191      * @return bool
192      */
193      public static function disallowsText(DOMElement $element)
194      {
195          return self::hasProperty($element, 'nt');
196      }
197   
198      /**
199      * Return the "allowChild" bitfield for given element
200      *
201      * @param  DOMElement $element
202      * @return string
203      */
204      public static function getAllowChildBitfield(DOMElement $element)
205      {
206          return self::getBitfield($element, 'ac');
207      }
208   
209      /**
210      * Return the "category" bitfield for given element
211      *
212      * @param  DOMElement $element
213      * @return string
214      */
215      public static function getCategoryBitfield(DOMElement $element)
216      {
217          return self::getBitfield($element, 'c');
218      }
219   
220      /**
221      * Return the "denyDescendant" bitfield for given element
222      *
223      * @param  DOMElement $element
224      * @return string
225      */
226      public static function getDenyDescendantBitfield(DOMElement $element)
227      {
228          return self::getBitfield($element, 'dd');
229      }
230   
231      /**
232      * Test whether given element is a block element
233      *
234      * @param  DOMElement $element
235      * @return bool
236      */
237      public static function isBlock(DOMElement $element)
238      {
239          return self::hasProperty($element, 'b');
240      }
241   
242      /**
243      * Test whether given element uses the empty content model
244      *
245      * @param  DOMElement $element
246      * @return bool
247      */
248      public static function isEmpty(DOMElement $element)
249      {
250          return self::hasProperty($element, 'e');
251      }
252   
253      /**
254      * Test whether given element is a formatting element
255      *
256      * @param  DOMElement $element
257      * @return bool
258      */
259      public static function isFormattingElement(DOMElement $element)
260      {
261          return self::hasProperty($element, 'fe');
262      }
263   
264      /**
265      * Test whether given element only accepts text nodes
266      *
267      * @param  DOMElement $element
268      * @return bool
269      */
270      public static function isTextOnly(DOMElement $element)
271      {
272          return self::hasProperty($element, 'to');
273      }
274   
275      /**
276      * Test whether given element uses the transparent content model
277      *
278      * @param  DOMElement $element
279      * @return bool
280      */
281      public static function isTransparent(DOMElement $element)
282      {
283          return self::hasProperty($element, 't');
284      }
285   
286      /**
287      * Test whether given element uses the void content model
288      *
289      * @param  DOMElement $element
290      * @return bool
291      */
292      public static function isVoid(DOMElement $element)
293      {
294          return self::hasProperty($element, 'v');
295      }
296   
297      /**
298      * Test whether given element preserves whitespace in its content
299      *
300      * @param  DOMElement $element
301      * @return bool
302      */
303      public static function preservesWhitespace(DOMElement $element)
304      {
305          return self::hasProperty($element, 'pre');
306      }
307   
308      /**
309      * Evaluate an XPath query using given element as context node
310      *
311      * @param  string     $query   XPath query
312      * @param  DOMElement $element Context node
313      * @return bool
314      */
315      protected static function evaluate($query, DOMElement $element)
316      {
317          $xpath = new DOMXPath($element->ownerDocument);
318   
319          return $xpath->evaluate('boolean(' . $query . ')', $element);
320      }
321   
322      /**
323      * Get the bitfield value for a given element
324      *
325      * @param  DOMElement $element Context node
326      * @param  string     $name    Bitfield name: either 'c', 'ac' or 'dd'
327      * @return string
328      */
329      protected static function getBitfield(DOMElement $element, $name)
330      {
331          $props    = self::getProperties($element);
332          $bitfield = self::toBin($props[$name]);
333   
334          // For each bit set to 1, test whether there is an XPath condition to it and whether it is
335          // fulfilled. If not, turn the bit to 0
336          foreach (array_keys(array_filter(str_split($bitfield, 1))) as $bitNumber)
337          {
338              $conditionName = $name . $bitNumber;
339              if (isset($props[$conditionName]) && !self::evaluate($props[$conditionName], $element))
340              {
341                  $bitfield[$bitNumber] = '0';
342              }
343          }
344   
345          return self::toRaw($bitfield);
346      }
347   
348      /**
349      * Return the properties associated with given element
350      *
351      * Returns span's properties if the element is not defined
352      *
353      * @param  DOMElement $element
354      * @return array
355      */
356      protected static function getProperties(DOMElement $element)
357      {
358          return self::$htmlElements[$element->nodeName] ?? self::$htmlElements['span'];
359      }
360   
361      /**
362      * Test whether given element has given property in context
363      *
364      * @param  DOMElement $element  Context node
365      * @param  string     $propName Property name, see self::$htmlElements
366      * @return bool
367      */
368      protected static function hasProperty(DOMElement $element, $propName)
369      {
370          $props = self::getProperties($element);
371   
372          return !empty($props[$propName]) && (!isset($props[$propName . '?']) || self::evaluate($props[$propName . '?'], $element));
373      }
374   
375      /**
376      * Convert a raw string to a series of 0 and 1 in LSB order
377      *
378      * @param  string $raw
379      * @return string
380      */
381      protected static function toBin($raw)
382      {
383          $bin = '';
384          foreach (str_split($raw, 1) as $char)
385          {
386              $bin .= strrev(substr('0000000' . decbin(ord($char)), -8));
387          }
388   
389          return $bin;
390      }
391   
392      /**
393      * Convert a series of 0 and 1 in LSB order to a raw string
394      *
395      * @param  string $bin
396      * @return string
397      */
398      protected static function toRaw($bin)
399      {
400          return implode('', array_map('chr', array_map('bindec', array_map('strrev', str_split($bin, 8)))));
401      }
402  }