Repository: ezyang/htmlpurifier (language: php)

Issue:
`DirectLex` will cause PHP error 
https://github.com/ezyang/htmlpurifier/blob/bbc513d79acf6691fa9cf10f192c90dd2957f18c/library/HTMLPurifier/Token/Tag.php#L47

Hello, 
When I use HTML Purifier with `Core.LexerImpl = DirectLex`, The PHP error will cause in above line when I purify the below data:
`<a href="https://example.com/" 10="hoge">Test</a>`
Because the key of attribute is `non-string` value. So the PHP error will be occured.
Ref: https://www.php.net/manual/en/function.ctype-lower.php

Please help to fix it.
I think we can fix it by using quick typecast in the `Tag.php` file:
`$key = (string)$key;` 

Or remove the attribute if the key is non-string value.

Thank you so much


Discussion / hints:
What is the value of `$attr` and `$key` if you dump the values? It's perhaps something to do with `10` not being a valid attribute name.
When I dump the value:
- The `$key` is `10` (non-string)
- The `$value` is `hoge `

In my example, the attribute key is `10` and the value is `hoge`. I know the attribute key `10` is invalid because it is a `non-string`. 
But my expected is:
- Output: `<a href="https://example.com/">Test</a>` (remove invalid attribute `10`)
- There is no PHP error or warning.

Fix patch:
diff --git a/library/HTMLPurifier/Token/Tag.php b/library/HTMLPurifier/Token/Tag.php
index d643fa64e..dcf365589 100644
--- a/library/HTMLPurifier/Token/Tag.php
+++ b/library/HTMLPurifier/Token/Tag.php
@@ -44,7 +44,7 @@ public function __construct($name, $attr = array(), $line = null, $col = null, $
         $this->name = ctype_lower($name) ? $name : strtolower($name);
         foreach ($attr as $key => $value) {
             // normalization only necessary when key is not lowercase
-            if (!ctype_lower($key)) {
+            if (!ctype_lower((string)$key)) {
                 $new_key = strtolower($key);
                 if (!isset($attr[$new_key])) {
                     $attr[$new_key] = $attr[$key];


Test patch:
diff --git a/tests/HTMLPurifier/HTMLDefinitionTest.php b/tests/HTMLPurifier/HTMLDefinitionTest.php
index 56d680f8b..3e8a1da72 100644
--- a/tests/HTMLPurifier/HTMLDefinitionTest.php
+++ b/tests/HTMLPurifier/HTMLDefinitionTest.php
@@ -222,6 +222,17 @@ public function test_AllowedAttributes_multipleErrors()
         $this->assertPurification_AllowedAttributes_local_p_style();
     }
 
+    public function test_AllowedAttributes_invalidAttributeDueToConsistingOfNumbers_UsingDirectLex()
+    {
+        $this->config->set('HTML.AllowedElements', array('a'));
+        $this->config->set('HTML.AllowedAttributes', 'href');
+        $this->config->set('Core.LexerImpl', 'DirectLex');
+        $this->assertPurification(
+            '<a href="https://example.com/" 10="hoge">Test</a>',
+            '<a href="https://example.com/">Test</a>'
+        );
+    }
+
     public function test_ForbiddenElements()
     {
         $this->config->set('HTML.ForbiddenElements', 'b');