phpcfdi / cfdi-cleaner
清理墨西哥CFDI
Requires
- php: >=7.3
- ext-dom: *
- ext-libxml: *
- symfony/polyfill-php80: ^1.22
Requires (Dev)
- ext-json: *
- phpunit/phpunit: ^9.3
README
用于清理墨西哥数字发票(CFDI)的工具。
🇺🇸 该项目的文档使用西班牙语,因为这是目标受众的自然语言。
关于 phpcfdi/cfdi-cleaner
数字发票(CFDI)的XML文件通常包含错误。这个库负责修复已知/常见的错误,以便可以处理它们。
这个库对CFDI的任何操作都不会影响原始字符串的生成或盖章。
安装
使用 composer
composer require phpcfdi/cfdi-cleaner
使用说明
工作类是 \PhpCfdi\CfdiCleaner\Cleaner
,并提供以下清理方法
staticClean(string $xml): string
从字符串进行文本和XML文档的清理,并返回清理后的文本表示。
此方法是静态的,因此不需要创建 Cleaner
对象的实例。
<?php use PhpCfdi\CfdiCleaner\Cleaner; $xml = file_get_contents('cfdi.xml'); echo Cleaner::staticClean($xml);
cleanStringToString(string $xml): string
从字符串进行文本和XML文档的清理,并返回清理后的文本表示。
<?php use PhpCfdi\CfdiCleaner\Cleaner; $xml = file_get_contents('cfdi.xml'); $cleaner = new Cleaner(); echo $cleaner->cleanStringToString($xml);
cleanStringToDocument(string $xml): DOMDocument
从字符串进行文本和XML文档的清理,并返回清理后的XML文档。
此方法非常有用,如果需要立即使用XML文档对象。
<?php use PhpCfdi\CfdiCleaner\Cleaner; $xml = file_get_contents('cfdi.xml'); $cleaner = new Cleaner(); $document = $cleaner->cleanStringToDocument($xml); echo $document->saveXML();
清理操作
可以进行两种类型的清理,一种是在尝试将XML内容加载为DOM对象之前对XML文本进行清理,另一种是在成功加载内容为DOM对象之后进行清理。
对XML文本的清理
这些清理器应在尝试读取XML内容之前执行,它们是为了防止无法创建文档对象。
RemoveNonXmlStrings
删除第一个字符 <
之前的所有内容以及最后一个字符 >
之后的全部内容。
SplitXmlDeclarationFromDocument
使用 LF
("\n"
) 将XML声明 <?xml version="1.0"?>
从XML主体中分开。
AppendXmlDeclaration
如果不存在,则在文件开始处添加 <?xml version="1.0"?>
,这非常有用,因为MIME检测工具如果不能识别文件头,则不会识别XML文件。
XmlNsSchemaLocation
删除SAT(墨西哥税务机关)发出的CFDI中经常出现的错误,其中使用 xmlns:schemaLocation
而不是 xsi:schemaLocation
。如果两者都存在,则只保留 xsi:schemaLocation
。
对XML文档(DOMDocument
)的清理
这些清理是在XML文档上进行的。
RemoveAddenda
从命名空间 http://www.sat.gob.mx/cfd/3
中删除任何 Addenda
类型的节点。
RemoveIncompleteSchemaLocations
作用于每个 xsi:schemaLocations
。
读取内容并尝试解释命名空间和验证方案的位置。为了验证它是一个验证方案,它检查是否以 .xsd
结尾(不区分大小写)。如果没有方案而没有命名空间,则忽略它。如果找到有方案而没有命名空间,则忽略它。
RemoveNonSatNamespacesNodes
检查所有命名空间定义,如果它们不属于URI http://www.sat.gob.mx/**
的命名空间,则删除相关节点和属性。
RemoveNonSatSchemaLocations
作用于每个 xsi:schemaLocations
。
验证命名空间定义,并删除所有不对应于URI http://www.sat.gob.mx/**
的命名空间对。
RemoveUnusedNamespaces
移除所有未使用的命名空间声明(包括其前缀)。
RenameElementAddPrefix
为未使用简单 xmlns
定义的前缀节点添加前缀。此外,删除多余的命名空间和冗余的 xmlns
定义。
不干净的CFDI示例
<cfdi:Comprobante xmlns="http://www.sat.gob.mx/cfd/4" xmlns:cfdi="http://www.sat.gob.mx/cfd/4"> <Emisor xmlns="http://www.sat.gob.mx/cfd/4" /> <cfdi:Receptor xmlns:cfdi="http://www.sat.gob.mx/cfd/4" /> </cfdi:Comprobante>
干净的CFDI示例
<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/4"> <cfdi:Emisor /> <cfdi:Receptor /> </cfdi:Comprobante>
MoveNamespaceDeclarationToRoot
将所有命名空间声明移动到根节点。
通常SAT要求在技术文档中在根节点定义命名空间,尽管它们通常定义在实现它们的节点上。
有些CFDI极端情况遵循XML规则,但不遵循CFDI规则,生成重叠的前缀。在这种情况下,仅将不重叠的命名空间移动,例如
<?xml version="1.0" encoding="utf-8" ?> <cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3"> <cfdi:Complemento> <cfdi:Otro xmlns:cfdi="http://www.sat.gob.mx/otro" /> <tfd:TimbreFiscalDigital xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital" /> </cfdi:Complemento> </cfdi:Comprobante>
生成以下结果
<?xml version="1.0" encoding="utf-8" ?> <cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital"> <cfdi:Complemento> <cfdi:Otro xmlns:cfdi="http://www.sat.gob.mx/otro" /> <tfd:TimbreFiscalDigital /> </cfdi:Complemento> </cfdi:Comprobante>
对于上述情况,未遵循附录20和补充中的既定规则。最好是始终将这种情况视为无效的CFDI,即使已签名,也应请求替换为包含正确命名空间前缀的CFDI。
MoveSchemaLocationsToRoot
将所有架构文件位置声明移动到主节点。
通常SAT要求在技术文档中在主节点定义架构文件位置,尽管它们通常定义在实现它们的节点上。
SetKnownSchemaLocations
验证已知命名空间架构的位置是否完全符合已知地址,如果不一致则进行修改。
以前,SAT允许架构文件位置在不区分大小写的情况下书写,甚至有多个位置来获取这些文件。然而,最近已经取消了对此类位置的容忍,并只允许官方定义。
此清理器根据项目 phpcfdi/sat-ns-registry 的信息,包含命名空间信息、应用版本和已知位置。
如果找不到命名空间的已知路径,则不会应用任何修正,并保留原始值。
CollapseComplemento
此清理器是为了解决SAT文档中的不一致性。
一方面,在CFDI 3.3的附录20中,SAT要求存在一个且仅存在一个 cfdi:Complemento
节点。然而,在验证XSD文件中允许存在多个。
通过这种清理,只保留一个包含所有补充的 cfdi:Complemento
节点。
清理器排除
为了不修改清理器的创建,并允许排除特定清理器,以及因此与库的新更新兼容,可以创建标准清理器,然后应用排除。
以下示例演示了如何排除影响 Addenda 的清理器。
<?php use PhpCfdi\CfdiCleaner\Cleaner; use PhpCfdi\CfdiCleaner\ExcludeList; use PhpCfdi\CfdiCleaner\XmlDocumentCleaners\RemoveAddenda, use PhpCfdi\CfdiCleaner\XmlDocumentCleaners\RemoveNonSatNamespacesNodes, use PhpCfdi\CfdiCleaner\XmlDocumentCleaners\RemoveNonSatSchemaLocations, /** * @var string $contents El contenido XML sucio. */ $exclude = new ExcludeList( RemoveAddenda::class, RemoveNonSatNamespacesNodes::class, RemoveNonSatSchemaLocations::class, ); $cleaner = new Cleaner(); $cleaner->exclude($exclude); $contents = $cleaner->cleanStringToString($contents);
支持
您可以通过在GitHub上创建工单来获得支持。
此外,此库属于 PhpCfdi 社区,因此您可以使用相同的通信渠道从社区成员那里获得帮助。
兼容性
此库将至少保持与最新的具有PHP活跃支持的版本兼容。
我们还使用语义化版本2.0.0,因此你可以放心使用此库而不用担心破坏你的应用程序。
贡献
欢迎贡献。请阅读CONTRIBUTING获取更多详细信息,并记得检查待办事项文件TODO以及变更日志文件CHANGELOG。
版权和许可证
phpcfdi/cfdi-cleaner
库版权所有© PhpCfdi,许可使用MIT许可证(MIT)。请参阅LICENSE获取更多信息。