This commit is contained in:
wyn
2026-05-26 18:01:32 +08:00
parent fa878334cd
commit 7a3a1ed8c9
651 changed files with 4599 additions and 32312 deletions

View File

@@ -45,6 +45,7 @@ jobs:
- '8.1'
- '8.2'
- '8.3'
- '8.4'
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -75,6 +76,7 @@ jobs:
- '8.1'
- '8.2'
- '8.3'
- '8.4'
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2

View File

@@ -48,7 +48,7 @@ Math is an open source project licensed under the terms of [MIT](https://github.
| **Simple** | Fraction | :material-check: | :material-check: |
| | Superscript | :material-check: | |
| **Architectural** | Row | :material-check: | :material-check: |
| | Semantics | | |
| | Semantics | :material-check: | |
## Contributing

View File

@@ -57,7 +57,9 @@ nav:
- Writers: 'usage/writers.md'
- Credits: 'credits.md'
- Releases:
- '0.1.0 (WIP)': 'changes/0.1.0.md'
- '0.3.0 (WIP)': 'changes/0.3.0.md'
- '0.2.0': 'changes/0.2.0.md'
- '0.1.0': 'changes/0.1.0.md'
- Developers:
- 'Coveralls': 'https://coveralls.io/github/PHPOffice/Math'
- 'Code Coverage': 'coverage/index.html'

View File

@@ -10,6 +10,7 @@ use PhpOffice\Math\Element;
use PhpOffice\Math\Exception\InvalidInputException;
use PhpOffice\Math\Exception\NotImplementedException;
use PhpOffice\Math\Math;
use PhpOffice\Math\Reader\Security\XmlScanner;
class MathML implements ReaderInterface
{
@@ -22,8 +23,17 @@ class MathML implements ReaderInterface
/** @var DOMXPath */
private $xpath;
/** @var XmlScanner */
private $xmlScanner;
public function __construct()
{
$this->xmlScanner = XmlScanner::getInstance();
}
public function read(string $content): ?Math
{
$content = $this->xmlScanner->scan($content);
$content = str_replace(
[
'⁢',
@@ -35,7 +45,7 @@ class MathML implements ReaderInterface
);
$this->dom = new DOMDocument();
$this->dom->loadXML($content, LIBXML_DTDLOAD);
$this->dom->loadXML($content);
$this->math = new Math();
$this->parseNode(null, $this->math);

View File

@@ -40,6 +40,26 @@ class MathML implements WriterInterface
{
$tagName = $this->getElementTagName($element);
// Element\AbstractGroupElement
if ($element instanceof Element\Semantics) {
$this->output->startElement($tagName);
// Write elements
foreach ($element->getElements() as $childElement) {
$this->writeElementItem($childElement);
}
// Write annotations
foreach ($element->getAnnotations() as $encoding => $annotation) {
$this->output->startElement('annotation');
$this->output->writeAttribute('encoding', $encoding);
$this->output->text($annotation);
$this->output->endElement();
}
$this->output->endElement();
return;
}
// Element\AbstractGroupElement
if ($element instanceof Element\AbstractGroupElement) {
$this->output->startElement($tagName);
@@ -121,6 +141,9 @@ class MathML implements WriterInterface
if ($element instanceof Element\Operator) {
return 'mo';
}
if ($element instanceof Element\Semantics) {
return 'semantics';
}
throw new NotImplementedException(sprintf(
'%s : The element of the class `%s` has no tag name',

View File

@@ -7,6 +7,7 @@ namespace Tests\PhpOffice\Math\Reader;
use PhpOffice\Math\Element;
use PhpOffice\Math\Exception\InvalidInputException;
use PhpOffice\Math\Exception\NotImplementedException;
use PhpOffice\Math\Exception\SecurityException;
use PhpOffice\Math\Math;
use PhpOffice\Math\Reader\MathML;
use PHPUnit\Framework\TestCase;
@@ -294,4 +295,15 @@ class MathMLTest extends TestCase
$reader = new MathML();
$math = $reader->read($content);
}
public function testReadSecurity(): void
{
$this->expectException(SecurityException::class);
$this->expectExceptionMessage('Detected use of ENTITY in XML, loading aborted to prevent XXE/XEE attacks');
$content = '<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE x SYSTEM "php://filter/convert.base64-decode/zlib.inflate/resource=data:,7Ztdb9owFIbv%2bRVZJ9armNjOZ2k7QUaL%2bRYO2nqFUnBFNQaMptP272cnNFuTsBbSskg1iATZzvGxn/ccX3A4fdfoecS7UsrK1A98hV5Rr9FVjlaz1UmlcnM7D9i6MlkufrB1AK79O2bqKltMllMWt96KL6ADwci7sJ4Yu0vr9/tlwKbqan27CPzrOXvevFGrbRvOGIseaCa7TAxok1x44xahXzQEcdKPKZPevap3RZw920I0VscWGLlU1efPsy0c5cbV1AoI7ZuOMCZW12nkcP9Q2%2bQObBNmL6ajg8s6xJqmJTrq5NIArX6zVk8Zcwwt4fPuLvHnbeBSvpdIQ6g93MvUv3CHqKNrmtEW4EYmCr5gDT5QzyNWE4x6xO1/aqQmgMhGYgaVDFUnScKltbFnaJoKHRuHK0L1pIkuaYselMe9cPUqRmm5C51u00kkhy1S3aBougkl7e4d6RGaTYeSehdCjAG/O/p%2bYfKyQsoLmgdlmsFYQFDjh6GWJyGE0ZfMX08EZtwNTdAYud7nLcksnwppA2UnqpCzgyDo1QadAU3vLOQZ82EHMxAi0KVcq7rzas5xD6AQoeqkYkgk02abukkJ/z%2bNvkj%2bjUy16Ba5d/S8anhBLwt44EgGkoFkIBlIBpKBZCAZSAaSgWQgGUgGkoFkIBlIBpKBZCAZSAaSgWQgGUgGxWOwW2nF7kt%2by7/Kb3ag2GUTUgBvXAAxiKxt4Is3sB4WniVrOvhwzB0CXerg5GN9esGRQv7RgQdMmMO9sIwtc/sIJUOCsY4ee7f7FIWu2Si4euKan8wg58nFsEIXxYGntgZqMog3Z2FrgPhgyzIOlsmijowqwb0jyMqMoGEbarqdOpP/iqFISMkSVFG1Z5p8f3OK%2bxAZ7gClpgUPg70rq0T2RIkcup/0newQ7NbcUXv/DPl4LL/N7hdfn2dp07pmd8v79YSdVVgwqcyWd8HC/8aOzkunf6r%2b2c8bpSxK/6uPmlf%2br/nSnyrHcduH99iqKiz7HwLxTLMgEM0QWUDjb3ji8NdHPslZmV%2bqR%2bfH56Xyxni1VGbV0m8=" []><foo></foo>M';
$reader = new MathML();
$math = $reader->read($content);
}
}

View File

@@ -80,6 +80,35 @@ class MathMLTest extends WriterTestCase
$this->assertIsSchemaMathMLValid($output);
}
public function testWriteSemantics(): void
{
$opTimes = new Element\Operator('&InvisibleTimes;');
$math = new Math();
$semantics = new Element\Semantics();
$semantics->add(new Element\Identifier('y'));
$semantics->addAnnotation('application/x-tex', ' y ');
$math->add($semantics);
$writer = new MathML();
$output = $writer->write($math);
$expected = '<?xml version="1.0" encoding="UTF-8"?>'
. PHP_EOL
. '<!DOCTYPE math PUBLIC "-//W3C//DTD MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/mathml2.dtd">'
. '<math xmlns="http://www.w3.org/1998/Math/MathML">'
. '<semantics>'
. '<mi>y</mi>'
. '<annotation encoding="application/x-tex"> y </annotation>'
. '</semantics>'
. '</math>'
. PHP_EOL;
$this->assertEquals($expected, $output);
$this->assertIsSchemaMathMLValid($output);
}
public function testWriteNotImplemented(): void
{
$this->expectException(NotImplementedException::class);