From cc55bd528d057226785c08a2924049a8c5ee900c Mon Sep 17 00:00:00 2001 From: wangjinlei <751475802@qq.com> Date: Wed, 3 Jun 2026 13:30:27 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=A0=A1=E5=AF=B9bug?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/common/TurnitinService.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/application/common/TurnitinService.php b/application/common/TurnitinService.php index 0e1dc9aa..47542d94 100644 --- a/application/common/TurnitinService.php +++ b/application/common/TurnitinService.php @@ -218,15 +218,16 @@ class TurnitinService } foreach ($candidates as $n) { - if ($n > 0 && $n <= 1.0) { - $scaled = round($n * 100, 2); - if ($scaled > 1.0 || $n < 0.05) { - return $scaled; - } + if ($n < 0) { + continue; } - if ($n >= 0) { - return round($n, 2); + // Turnitin TCA 的 overall_match_percentage 是 0–100 整数,"1" 即代表 1%。 + // 仅当值是「严格小于 1 的非整数」(真正的 0–1 小数比例,如 0.12=12%)时才 ×100, + // 避免把整数 1(1%)误判成 100%。 + if ($n > 0 && $n < 1.0) { + return round(min($n * 100, 100), 2); } + return round(min($n, 100), 2); } return 0.0; From bbd690ca0f68c671ece05e82edc88ee7a68b82ed Mon Sep 17 00:00:00 2001 From: wangjinlei <751475802@qq.com> Date: Thu, 4 Jun 2026 09:35:19 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E5=B0=8F=E9=97=AE=E9=A2=98=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/api/job/UserFieldAiFill.php | 2 +- application/common/PromotionService.php | 166 ++++++++++++++++++------ 2 files changed, 127 insertions(+), 41 deletions(-) diff --git a/application/api/job/UserFieldAiFill.php b/application/api/job/UserFieldAiFill.php index deb1b447..acb0dcd6 100644 --- a/application/api/job/UserFieldAiFill.php +++ b/application/api/job/UserFieldAiFill.php @@ -30,6 +30,6 @@ class UserFieldAiFill $job->delete(); $delay = max(0, (int) (isset($data['delay']) ? $data['delay'] : 1)); -// $svc->enqueueNextFieldAi($delay, $queue, $userId, $force); + $svc->enqueueNextFieldAi($delay, $queue, $userId, $force); } } diff --git a/application/common/PromotionService.php b/application/common/PromotionService.php index d958bed2..c5b1ad2e 100644 --- a/application/common/PromotionService.php +++ b/application/common/PromotionService.php @@ -509,47 +509,66 @@ class PromotionService } } - // 一次 LLM 调用生成两段内容(description + advised_topics) - $llmResult = [ - 'description' => '', - 'description_status' => 0, - 'advised_topics' => '', - 'advised_topics_status' => 0, - ]; - try { - $llm = new PromotionLlmService(); - $llmResult = $llm->generateEmailContent( - $expert, - $journal ?: [], - $overlapFields, - $taskFields, - $fieldSet - ); - } catch (\Exception $e) { - $fbDesc = ''; - $fbAdvised = ''; - try { - $fbSvc = isset($llm) ? $llm : new PromotionLlmService(); - $fbDesc = $fbSvc->getFallback(); - $fbAdvised = $fbSvc->getAdvisedFallback(); - } catch (\Exception $ignore) { - } - $llmResult = [ - 'description' => $fbDesc, - 'description_status' => 2, - 'advised_topics' => $fbAdvised, - 'advised_topics_status' => 2, - ]; - $this->log("prepareSingleEmail log_id={$logId} llm_exception=" . $e->getMessage()); - } - $llmText = (string)$llmResult['description']; - $llmStatus = intval($llmResult['description_status']); - $advisedText = (string)$llmResult['advised_topics']; - $advisedStatus = intval($llmResult['advised_topics_status']); + // 仅当模板真正引用了 LLM 占位符(llm_description / ai_content_analysis / + // ai_advised_topics / llm_advised_topics)时才调用 LLM,避免无谓的请求与开销。 + $llmNeed = $this->detectLlmTemplateNeed( + $task['template_id'], + $task['journal_id'], + intval($task['style_id'] ?? 0) + ); - $expert['llm_description'] = $llmText; - $expert['ai_advised_topics'] = $advisedText; - $expert['role'] = $this->mapExpertTypeRole($expertType); + if (!$llmNeed['need']) { + $llmText = ''; + $llmStatus = 0; + $advisedText = ''; + $advisedStatus = 0; + $expert['llm_description'] = ''; + $expert['ai_advised_topics'] = ''; + $expert['role'] = $this->mapExpertTypeRole($expertType); + $this->log("prepareSingleEmail log_id={$logId} skip_llm (template has no llm placeholders)"); + } else { + // 一次 LLM 调用生成两段内容(description + advised_topics) + $llmResult = [ + 'description' => '', + 'description_status' => 0, + 'advised_topics' => '', + 'advised_topics_status' => 0, + ]; + try { + $llm = new PromotionLlmService(); + $llmResult = $llm->generateEmailContent( + $expert, + $journal ?: [], + $overlapFields, + $taskFields, + $fieldSet + ); + } catch (\Exception $e) { + $fbDesc = ''; + $fbAdvised = ''; + try { + $fbSvc = isset($llm) ? $llm : new PromotionLlmService(); + $fbDesc = $fbSvc->getFallback(); + $fbAdvised = $fbSvc->getAdvisedFallback(); + } catch (\Exception $ignore) { + } + $llmResult = [ + 'description' => $fbDesc, + 'description_status' => 2, + 'advised_topics' => $fbAdvised, + 'advised_topics_status' => 2, + ]; + $this->log("prepareSingleEmail log_id={$logId} llm_exception=" . $e->getMessage()); + } + $llmText = (string)$llmResult['description']; + $llmStatus = intval($llmResult['description_status']); + $advisedText = (string)$llmResult['advised_topics']; + $advisedStatus = intval($llmResult['advised_topics_status']); + + $expert['llm_description'] = $llmText; + $expert['ai_advised_topics'] = $advisedText; + $expert['role'] = $this->mapExpertTypeRole($expertType); + } } $expertVars = $this->buildExpertVars($expert); @@ -935,6 +954,73 @@ class PromotionService // ==================== Template Rendering ==================== + /** + * 检测邮件模板(含 style 头尾)是否包含需要 LLM 生成的占位符。 + * + * @return array{need:bool,need_description:bool,need_advised_topics:bool,tags:array} + */ + public function detectLlmTemplateNeed($templateId, $journalId, $styleId = 0) + { + $descriptionTags = ['llm_description', 'ai_content_analysis']; + $advisedTags = ['ai_advised_topics', 'llm_advised_topics']; + $allTags = array_merge($descriptionTags, $advisedTags); + + $parts = []; + $tpl = Db::name('mail_template') + ->where('template_id', $templateId) + ->where('journal_id', $journalId) + ->where('state', 0) + ->find(); + if ($tpl) { + $parts[] = (string)($tpl['subject'] ?? ''); + $parts[] = (string)($tpl['body_html'] ?? ''); + } + $styleId = intval($styleId); + if ($styleId > 0) { + $style = Db::name('mail_style')->where('style_id', $styleId)->where('state', 0)->find(); + if ($style) { + $parts[] = (string)($style['header_html'] ?? ''); + $parts[] = (string)($style['footer_html'] ?? ''); + } + } + + $haystack = implode("\n", $parts); + $found = []; + foreach ($allTags as $tag) { + if ($this->templateContainsVar($haystack, $tag)) { + $found[] = $tag; + } + } + + $needDesc = (bool) array_intersect($found, $descriptionTags); + $needAdvised = (bool) array_intersect($found, $advisedTags); + + return [ + 'need' => !empty($found), + 'need_description' => $needDesc, + 'need_advised_topics' => $needAdvised, + 'tags' => $found, + ]; + } + + /** + * 模板是否包含某变量占位符(支持 {{ var }} 与 {var})。 + */ + protected function templateContainsVar($haystack, $varName) + { + if (!is_string($haystack) || $haystack === '' || $varName === '') { + return false; + } + $quoted = preg_quote($varName, '/'); + if (preg_match('/\{\{\s*' . $quoted . '\s*\}\}/', $haystack)) { + return true; + } + if (strpos($haystack, '{' . $varName . '}') !== false) { + return true; + } + return false; + } + public function renderFromTemplate($templateId, $journalId, $varsJson, $styleId = 0) { $tpl = Db::name('mail_template')->where('template_id', $templateId)->where('journal_id', $journalId)->where('state', 0)->find(); From 0ee7c380000b8d2e995491760d848dfdbcbb9ae3 Mon Sep 17 00:00:00 2001 From: wangjinlei <751475802@qq.com> Date: Thu, 4 Jun 2026 13:33:13 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E6=A1=86=E6=9E=B6=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E8=BF=90=E8=A1=8C=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=B0=9D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/controller/BackgroundCheck.php | 386 +++++++++ application/common/BackgroundCheckService.php | 705 +++++++++++++++ composer.json | 5 +- vendor/autoload.php | 5 +- vendor/composer/autoload_classmap.php | 5 - vendor/composer/autoload_files.php | 4 +- vendor/composer/autoload_psr4.php | 5 +- vendor/composer/autoload_real.php | 12 +- vendor/composer/autoload_static.php | 35 +- vendor/composer/installed.json | 808 +++++++++++------- vendor/composer/installed.php | 145 ++-- vendor/composer/platform_check.php | 25 - vendor/phpmailer/phpmailer/README.md | 2 +- vendor/phpmailer/phpmailer/VERSION | 2 +- vendor/phpmailer/phpmailer/composer.json | 6 +- .../phpmailer/language/phpmailer.lang-es.php | 6 +- vendor/phpmailer/phpmailer/src/PHPMailer.php | 374 +++++--- vendor/phpmailer/phpmailer/src/POP3.php | 2 +- vendor/phpmailer/phpmailer/src/SMTP.php | 53 +- .../phpoffice/math/.github/workflows/php.yml | 2 - vendor/phpoffice/math/docs/index.md | 2 +- vendor/phpoffice/math/mkdocs.yml | 4 +- .../phpoffice/math/src/Math/Reader/MathML.php | 12 +- .../phpoffice/math/src/Math/Writer/MathML.php | 23 - .../math/tests/Math/Reader/MathMLTest.php | 12 - .../math/tests/Math/Writer/MathMLTest.php | 29 - vendor/phpoffice/phpword/LICENSE | 2 +- vendor/phpoffice/phpword/README.md | 11 +- vendor/phpoffice/phpword/composer.json | 87 +- vendor/phpoffice/phpword/mkdocs.yml | 5 +- .../phpoffice/phpword/phpstan-baseline.neon | 140 ++- vendor/phpoffice/phpword/phpword.ini.dist | 1 - .../PhpWord/Collection/AbstractCollection.php | 2 - .../src/PhpWord/Collection/Bookmarks.php | 2 - .../phpword/src/PhpWord/Collection/Charts.php | 2 - .../src/PhpWord/Collection/Comments.php | 2 - .../src/PhpWord/Collection/Endnotes.php | 2 - .../src/PhpWord/Collection/Footnotes.php | 2 - .../phpword/src/PhpWord/Collection/Titles.php | 2 - .../ComplexType/FootnoteProperties.php | 1 - .../src/PhpWord/ComplexType/ProofState.php | 1 - .../src/PhpWord/ComplexType/TblWidth.php | 1 - .../PhpWord/ComplexType/TrackChangesView.php | 1 - .../src/PhpWord/Element/AbstractContainer.php | 22 +- .../src/PhpWord/Element/AbstractElement.php | 13 +- .../phpword/src/PhpWord/Element/Bookmark.php | 1 - .../phpword/src/PhpWord/Element/Cell.php | 7 +- .../phpword/src/PhpWord/Element/Chart.php | 5 +- .../phpword/src/PhpWord/Element/CheckBox.php | 1 - .../phpword/src/PhpWord/Element/Comment.php | 5 +- .../phpword/src/PhpWord/Element/Endnote.php | 1 - .../phpword/src/PhpWord/Element/Field.php | 11 +- .../phpword/src/PhpWord/Element/Footer.php | 1 - .../phpword/src/PhpWord/Element/Footnote.php | 7 +- .../phpword/src/PhpWord/Element/FormField.php | 1 - .../phpword/src/PhpWord/Element/Formula.php | 1 - .../phpword/src/PhpWord/Element/Header.php | 1 - .../phpword/src/PhpWord/Element/Image.php | 1 - .../phpword/src/PhpWord/Element/Line.php | 5 +- .../phpword/src/PhpWord/Element/Link.php | 13 +- .../phpword/src/PhpWord/Element/ListItem.php | 9 +- .../src/PhpWord/Element/ListItemRun.php | 5 +- .../phpword/src/PhpWord/Element/OLEObject.php | 5 +- .../phpword/src/PhpWord/Element/PageBreak.php | 1 - .../src/PhpWord/Element/PreserveText.php | 9 +- .../phpword/src/PhpWord/Element/Row.php | 11 +- .../phpword/src/PhpWord/Element/SDT.php | 1 - .../phpword/src/PhpWord/Element/Section.php | 9 +- .../phpword/src/PhpWord/Element/Shape.php | 5 +- .../phpword/src/PhpWord/Element/TOC.php | 11 +- .../phpword/src/PhpWord/Element/Table.php | 15 +- .../phpword/src/PhpWord/Element/Text.php | 23 +- .../phpword/src/PhpWord/Element/TextBox.php | 5 +- .../phpword/src/PhpWord/Element/TextBreak.php | 15 +- .../phpword/src/PhpWord/Element/TextRun.php | 4 - .../phpword/src/PhpWord/Element/Title.php | 1 - .../src/PhpWord/Element/TrackChange.php | 1 - .../src/PhpWord/Escaper/AbstractEscaper.php | 1 - .../src/PhpWord/Escaper/EscaperInterface.php | 1 - .../phpword/src/PhpWord/Escaper/RegExp.php | 1 - .../phpword/src/PhpWord/Escaper/Rtf.php | 1 - .../phpword/src/PhpWord/Escaper/Xml.php | 1 - .../PhpWord/Exception/CopyFileException.php | 1 - .../CreateTemporaryFileException.php | 1 - .../src/PhpWord/Exception/Exception.php | 1 - .../Exception/InvalidImageException.php | 1 - .../Exception/InvalidObjectException.php | 1 - .../Exception/InvalidStyleException.php | 1 - .../UnsupportedImageTypeException.php | 1 - .../phpword/src/PhpWord/IOFactory.php | 13 +- .../phpoffice/phpword/src/PhpWord/Media.php | 1 - .../src/PhpWord/Metadata/Compatibility.php | 1 - .../phpword/src/PhpWord/Metadata/DocInfo.php | 1 - .../src/PhpWord/Metadata/Protection.php | 1 - .../phpword/src/PhpWord/Metadata/Settings.php | 7 +- .../phpoffice/phpword/src/PhpWord/PhpWord.php | 57 +- .../src/PhpWord/Reader/AbstractReader.php | 1 - .../phpword/src/PhpWord/Reader/HTML.php | 3 +- .../phpword/src/PhpWord/Reader/MsDoc.php | 1 - .../phpword/src/PhpWord/Reader/ODText.php | 5 +- .../PhpWord/Reader/ODText/AbstractPart.php | 1 - .../src/PhpWord/Reader/ODText/Content.php | 1 - .../src/PhpWord/Reader/ODText/Meta.php | 1 - .../phpword/src/PhpWord/Reader/RTF.php | 3 +- .../src/PhpWord/Reader/RTF/Document.php | 3 +- .../src/PhpWord/Reader/ReaderInterface.php | 1 - .../phpword/src/PhpWord/Reader/Word2007.php | 3 +- .../PhpWord/Reader/Word2007/AbstractPart.php | 56 +- .../PhpWord/Reader/Word2007/DocPropsApp.php | 1 - .../PhpWord/Reader/Word2007/DocPropsCore.php | 1 - .../Reader/Word2007/DocPropsCustom.php | 1 - .../src/PhpWord/Reader/Word2007/Document.php | 3 +- .../src/PhpWord/Reader/Word2007/Endnotes.php | 1 - .../src/PhpWord/Reader/Word2007/Footnotes.php | 1 - .../src/PhpWord/Reader/Word2007/Numbering.php | 1 - .../src/PhpWord/Reader/Word2007/Settings.php | 1 - .../src/PhpWord/Reader/Word2007/Styles.php | 4 - .../phpword/src/PhpWord/Settings.php | 58 +- .../src/PhpWord/Shared/AbstractEnum.php | 1 - .../phpword/src/PhpWord/Shared/Converter.php | 1 - .../phpword/src/PhpWord/Shared/Css.php | 5 +- .../phpword/src/PhpWord/Shared/Drawing.php | 1 - .../phpword/src/PhpWord/Shared/Html.php | 282 ++---- .../Shared/Microsoft/PasswordEncoder.php | 8 +- .../phpword/src/PhpWord/Shared/Text.php | 6 - .../phpword/src/PhpWord/Shared/Validate.php | 1 - .../phpword/src/PhpWord/Shared/XMLReader.php | 1 - .../phpword/src/PhpWord/Shared/XMLWriter.php | 1 - .../phpword/src/PhpWord/Shared/ZipArchive.php | 18 +- .../phpword/src/PhpWord/SimpleType/Border.php | 1 - .../src/PhpWord/SimpleType/DocProtect.php | 1 - .../phpword/src/PhpWord/SimpleType/Jc.php | 3 +- .../src/PhpWord/SimpleType/JcTable.php | 1 - .../PhpWord/SimpleType/LineSpacingRule.php | 1 - .../src/PhpWord/SimpleType/NumberFormat.php | 1 - .../src/PhpWord/SimpleType/TblWidth.php | 1 - .../src/PhpWord/SimpleType/TextAlignment.php | 1 - .../src/PhpWord/SimpleType/VerticalJc.php | 1 - .../phpword/src/PhpWord/SimpleType/Zoom.php | 1 - .../phpoffice/phpword/src/PhpWord/Style.php | 15 +- .../src/PhpWord/Style/AbstractStyle.php | 10 +- .../phpword/src/PhpWord/Style/Border.php | 1 - .../phpword/src/PhpWord/Style/Cell.php | 129 +-- .../phpword/src/PhpWord/Style/Chart.php | 1 - .../phpword/src/PhpWord/Style/Extrusion.php | 1 - .../phpword/src/PhpWord/Style/Fill.php | 1 - .../phpword/src/PhpWord/Style/Font.php | 23 +- .../phpword/src/PhpWord/Style/Frame.php | 1 - .../phpword/src/PhpWord/Style/Image.php | 1 - .../phpword/src/PhpWord/Style/Indentation.php | 84 +- .../phpword/src/PhpWord/Style/Language.php | 1 - .../phpword/src/PhpWord/Style/Line.php | 1 - .../src/PhpWord/Style/LineNumbering.php | 1 - .../phpword/src/PhpWord/Style/ListItem.php | 1 - .../phpword/src/PhpWord/Style/Numbering.php | 29 +- .../src/PhpWord/Style/NumberingLevel.php | 1 - .../phpword/src/PhpWord/Style/Outline.php | 1 - .../phpword/src/PhpWord/Style/Paper.php | 1 - .../phpword/src/PhpWord/Style/Paragraph.php | 151 +--- .../phpword/src/PhpWord/Style/Row.php | 1 - .../phpword/src/PhpWord/Style/Section.php | 11 +- .../phpword/src/PhpWord/Style/Shading.php | 1 - .../phpword/src/PhpWord/Style/Shadow.php | 1 - .../phpword/src/PhpWord/Style/Shape.php | 21 +- .../phpword/src/PhpWord/Style/Spacing.php | 1 - .../phpword/src/PhpWord/Style/TOC.php | 1 - .../phpword/src/PhpWord/Style/Tab.php | 1 - .../phpword/src/PhpWord/Style/Table.php | 13 +- .../src/PhpWord/Style/TablePosition.php | 1 - .../phpword/src/PhpWord/Style/TextBox.php | 1 - .../phpword/src/PhpWord/TemplateProcessor.php | 26 +- .../src/PhpWord/Writer/AbstractWriter.php | 11 +- .../phpword/src/PhpWord/Writer/HTML.php | 3 +- .../Writer/HTML/Element/AbstractElement.php | 3 +- .../PhpWord/Writer/HTML/Element/Bookmark.php | 1 - .../PhpWord/Writer/HTML/Element/Container.php | 3 +- .../PhpWord/Writer/HTML/Element/Endnote.php | 1 - .../PhpWord/Writer/HTML/Element/Footnote.php | 1 - .../src/PhpWord/Writer/HTML/Element/Image.php | 1 - .../src/PhpWord/Writer/HTML/Element/Link.php | 1 - .../PhpWord/Writer/HTML/Element/ListItem.php | 1 - .../Writer/HTML/Element/ListItemRun.php | 1 - .../PhpWord/Writer/HTML/Element/PageBreak.php | 1 - .../src/PhpWord/Writer/HTML/Element/Table.php | 1 - .../src/PhpWord/Writer/HTML/Element/Text.php | 3 +- .../PhpWord/Writer/HTML/Element/TextBreak.php | 1 - .../PhpWord/Writer/HTML/Element/TextRun.php | 1 - .../src/PhpWord/Writer/HTML/Element/Title.php | 1 - .../PhpWord/Writer/HTML/Part/AbstractPart.php | 1 - .../src/PhpWord/Writer/HTML/Part/Body.php | 3 +- .../src/PhpWord/Writer/HTML/Part/Head.php | 6 +- .../Writer/HTML/Style/AbstractStyle.php | 1 - .../src/PhpWord/Writer/HTML/Style/Font.php | 1 - .../src/PhpWord/Writer/HTML/Style/Generic.php | 1 - .../src/PhpWord/Writer/HTML/Style/Image.php | 1 - .../PhpWord/Writer/HTML/Style/Paragraph.php | 1 - .../src/PhpWord/Writer/HTML/Style/Table.php | 4 - .../phpword/src/PhpWord/Writer/ODText.php | 3 +- .../Writer/ODText/Element/AbstractElement.php | 29 - .../Writer/ODText/Element/Container.php | 6 - .../PhpWord/Writer/ODText/Element/Field.php | 1 - .../PhpWord/Writer/ODText/Element/Formula.php | 1 - .../PhpWord/Writer/ODText/Element/Image.php | 18 +- .../PhpWord/Writer/ODText/Element/Link.php | 1 - .../Writer/ODText/Element/PageBreak.php | 1 - .../PhpWord/Writer/ODText/Element/Table.php | 5 +- .../PhpWord/Writer/ODText/Element/Text.php | 70 +- .../Writer/ODText/Element/TextBreak.php | 1 - .../PhpWord/Writer/ODText/Element/TextRun.php | 1 - .../PhpWord/Writer/ODText/Element/Title.php | 1 - .../Writer/ODText/Part/AbstractPart.php | 1 - .../PhpWord/Writer/ODText/Part/Content.php | 13 +- .../PhpWord/Writer/ODText/Part/Manifest.php | 1 - .../src/PhpWord/Writer/ODText/Part/Meta.php | 1 - .../PhpWord/Writer/ODText/Part/Mimetype.php | 1 - .../src/PhpWord/Writer/ODText/Part/Styles.php | 4 +- .../Writer/ODText/Style/AbstractStyle.php | 1 - .../src/PhpWord/Writer/ODText/Style/Font.php | 3 +- .../src/PhpWord/Writer/ODText/Style/Image.php | 1 - .../PhpWord/Writer/ODText/Style/Paragraph.php | 9 +- .../PhpWord/Writer/ODText/Style/Section.php | 1 - .../src/PhpWord/Writer/ODText/Style/Table.php | 1 - .../phpword/src/PhpWord/Writer/PDF.php | 3 +- .../PhpWord/Writer/PDF/AbstractRenderer.php | 1 - .../phpword/src/PhpWord/Writer/PDF/DomPDF.php | 1 - .../phpword/src/PhpWord/Writer/PDF/MPDF.php | 1 - .../phpword/src/PhpWord/Writer/PDF/TCPDF.php | 1 - .../phpword/src/PhpWord/Writer/RTF.php | 3 +- .../Writer/RTF/Element/AbstractElement.php | 11 +- .../PhpWord/Writer/RTF/Element/Container.php | 1 - .../src/PhpWord/Writer/RTF/Element/Field.php | 1 - .../src/PhpWord/Writer/RTF/Element/Image.php | 1 - .../src/PhpWord/Writer/RTF/Element/Link.php | 1 - .../PhpWord/Writer/RTF/Element/ListItem.php | 1 - .../PhpWord/Writer/RTF/Element/PageBreak.php | 1 - .../src/PhpWord/Writer/RTF/Element/Table.php | 1 - .../src/PhpWord/Writer/RTF/Element/Text.php | 1 - .../PhpWord/Writer/RTF/Element/TextBreak.php | 1 - .../PhpWord/Writer/RTF/Element/TextRun.php | 1 - .../src/PhpWord/Writer/RTF/Element/Title.php | 10 +- .../PhpWord/Writer/RTF/Part/AbstractPart.php | 1 - .../src/PhpWord/Writer/RTF/Part/Document.php | 9 +- .../src/PhpWord/Writer/RTF/Part/Header.php | 5 +- .../Writer/RTF/Style/AbstractStyle.php | 1 - .../src/PhpWord/Writer/RTF/Style/Border.php | 1 - .../src/PhpWord/Writer/RTF/Style/Font.php | 1 - .../PhpWord/Writer/RTF/Style/Indentation.php | 1 - .../PhpWord/Writer/RTF/Style/Paragraph.php | 1 - .../src/PhpWord/Writer/RTF/Style/Section.php | 1 - .../src/PhpWord/Writer/RTF/Style/Tab.php | 1 - .../phpword/src/PhpWord/Writer/Word2007.php | 11 +- .../Word2007/Element/AbstractElement.php | 13 +- .../Writer/Word2007/Element/Bookmark.php | 1 - .../PhpWord/Writer/Word2007/Element/Chart.php | 1 - .../Writer/Word2007/Element/CheckBox.php | 1 - .../Writer/Word2007/Element/Container.php | 18 +- .../Writer/Word2007/Element/Endnote.php | 1 - .../PhpWord/Writer/Word2007/Element/Field.php | 5 +- .../Writer/Word2007/Element/Footnote.php | 1 - .../Writer/Word2007/Element/FormField.php | 1 - .../Writer/Word2007/Element/Formula.php | 1 - .../PhpWord/Writer/Word2007/Element/Image.php | 1 - .../PhpWord/Writer/Word2007/Element/Line.php | 1 - .../PhpWord/Writer/Word2007/Element/Link.php | 1 - .../Writer/Word2007/Element/ListItem.php | 1 - .../Writer/Word2007/Element/ListItemRun.php | 1 - .../Writer/Word2007/Element/OLEObject.php | 1 - .../Writer/Word2007/Element/PageBreak.php | 1 - .../Word2007/Element/ParagraphAlignment.php | 1 - .../Writer/Word2007/Element/PreserveText.php | 1 - .../PhpWord/Writer/Word2007/Element/SDT.php | 1 - .../PhpWord/Writer/Word2007/Element/Shape.php | 1 - .../PhpWord/Writer/Word2007/Element/TOC.php | 3 +- .../PhpWord/Writer/Word2007/Element/Table.php | 1 - .../Word2007/Element/TableAlignment.php | 1 - .../PhpWord/Writer/Word2007/Element/Text.php | 1 - .../Writer/Word2007/Element/TextBox.php | 8 - .../Writer/Word2007/Element/TextBreak.php | 1 - .../Writer/Word2007/Element/TextRun.php | 1 - .../PhpWord/Writer/Word2007/Element/Title.php | 1 - .../Writer/Word2007/Part/AbstractPart.php | 7 +- .../PhpWord/Writer/Word2007/Part/Chart.php | 3 +- .../PhpWord/Writer/Word2007/Part/Comments.php | 5 +- .../Writer/Word2007/Part/ContentTypes.php | 3 +- .../Writer/Word2007/Part/DocPropsApp.php | 1 - .../Writer/Word2007/Part/DocPropsCore.php | 1 - .../Writer/Word2007/Part/DocPropsCustom.php | 1 - .../PhpWord/Writer/Word2007/Part/Document.php | 1 - .../PhpWord/Writer/Word2007/Part/Endnotes.php | 1 - .../Writer/Word2007/Part/FontTable.php | 1 - .../PhpWord/Writer/Word2007/Part/Footer.php | 1 - .../Writer/Word2007/Part/Footnotes.php | 3 +- .../PhpWord/Writer/Word2007/Part/Header.php | 1 - .../Writer/Word2007/Part/Numbering.php | 3 +- .../src/PhpWord/Writer/Word2007/Part/Rels.php | 1 - .../Writer/Word2007/Part/RelsDocument.php | 1 - .../PhpWord/Writer/Word2007/Part/RelsPart.php | 1 - .../PhpWord/Writer/Word2007/Part/Settings.php | 3 +- .../PhpWord/Writer/Word2007/Part/Styles.php | 12 +- .../PhpWord/Writer/Word2007/Part/Theme.php | 1 - .../Writer/Word2007/Part/WebSettings.php | 1 - .../Writer/Word2007/Style/AbstractStyle.php | 7 +- .../PhpWord/Writer/Word2007/Style/Cell.php | 35 - .../Writer/Word2007/Style/Extrusion.php | 1 - .../PhpWord/Writer/Word2007/Style/Fill.php | 1 - .../PhpWord/Writer/Word2007/Style/Font.php | 1 - .../PhpWord/Writer/Word2007/Style/Frame.php | 1 - .../PhpWord/Writer/Word2007/Style/Image.php | 1 - .../Writer/Word2007/Style/Indentation.php | 4 - .../PhpWord/Writer/Word2007/Style/Line.php | 1 - .../Writer/Word2007/Style/LineNumbering.php | 1 - .../Writer/Word2007/Style/MarginBorder.php | 1 - .../PhpWord/Writer/Word2007/Style/Outline.php | 1 - .../Writer/Word2007/Style/Paragraph.php | 5 +- .../src/PhpWord/Writer/Word2007/Style/Row.php | 1 - .../PhpWord/Writer/Word2007/Style/Section.php | 1 - .../PhpWord/Writer/Word2007/Style/Shading.php | 1 - .../PhpWord/Writer/Word2007/Style/Shadow.php | 1 - .../PhpWord/Writer/Word2007/Style/Shape.php | 1 - .../PhpWord/Writer/Word2007/Style/Spacing.php | 1 - .../src/PhpWord/Writer/Word2007/Style/Tab.php | 1 - .../PhpWord/Writer/Word2007/Style/Table.php | 1 - .../Writer/Word2007/Style/TablePosition.php | 1 - .../PhpWord/Writer/Word2007/Style/TextBox.php | 1 - .../src/PhpWord/Writer/WriterInterface.php | 1 - vendor/psr/http-message/composer.json | 4 +- .../psr/http-message/src/MessageInterface.php | 24 +- .../psr/http-message/src/RequestInterface.php | 15 +- .../http-message/src/ResponseInterface.php | 8 +- .../src/ServerRequestInterface.php | 24 +- .../psr/http-message/src/StreamInterface.php | 28 +- .../src/UploadedFileInterface.php | 14 +- vendor/psr/http-message/src/UriInterface.php | 34 +- vendor/psr/log/Psr/Log/AbstractLogger.php | 128 --- .../log/Psr/Log/InvalidArgumentException.php | 7 - vendor/psr/log/Psr/Log/LogLevel.php | 18 - .../psr/log/Psr/Log/LoggerAwareInterface.php | 18 - vendor/psr/log/Psr/Log/LoggerAwareTrait.php | 26 - vendor/psr/log/Psr/Log/LoggerInterface.php | 125 --- vendor/psr/log/Psr/Log/LoggerTrait.php | 142 --- vendor/psr/log/Psr/Log/NullLogger.php | 30 - vendor/psr/log/Psr/Log/Test/DummyTest.php | 18 - .../log/Psr/Log/Test/LoggerInterfaceTest.php | 138 --- vendor/psr/log/Psr/Log/Test/TestLogger.php | 147 ---- vendor/psr/log/composer.json | 6 +- vendor/topthink/think-image/src/Image.php | 122 +-- .../think-image/src/image/Exception.php | 3 +- .../think-image/src/image/gif/Decoder.php | 3 +- .../think-image/src/image/gif/Encoder.php | 6 +- .../think-image/src/image/gif/Gif.php | 2 +- .../topthink/think-image/tests/CropTest.php | 2 +- .../topthink/think-image/tests/FlipTest.php | 3 +- .../topthink/think-image/tests/InfoTest.php | 3 +- .../topthink/think-image/tests/RotateTest.php | 2 +- .../topthink/think-image/tests/TestCase.php | 2 +- .../topthink/think-image/tests/TextTest.php | 2 +- .../topthink/think-image/tests/ThumbTest.php | 3 +- .../topthink/think-image/tests/WaterTest.php | 2 +- .../topthink/think-image/tests/autoload.php | 2 +- 359 files changed, 2767 insertions(+), 3114 deletions(-) create mode 100644 application/api/controller/BackgroundCheck.php create mode 100644 application/common/BackgroundCheckService.php delete mode 100644 vendor/composer/platform_check.php delete mode 100644 vendor/psr/log/Psr/Log/AbstractLogger.php delete mode 100644 vendor/psr/log/Psr/Log/InvalidArgumentException.php delete mode 100644 vendor/psr/log/Psr/Log/LogLevel.php delete mode 100644 vendor/psr/log/Psr/Log/LoggerAwareInterface.php delete mode 100644 vendor/psr/log/Psr/Log/LoggerAwareTrait.php delete mode 100644 vendor/psr/log/Psr/Log/LoggerInterface.php delete mode 100644 vendor/psr/log/Psr/Log/LoggerTrait.php delete mode 100644 vendor/psr/log/Psr/Log/NullLogger.php delete mode 100644 vendor/psr/log/Psr/Log/Test/DummyTest.php delete mode 100644 vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php delete mode 100644 vendor/psr/log/Psr/Log/Test/TestLogger.php diff --git a/application/api/controller/BackgroundCheck.php b/application/api/controller/BackgroundCheck.php new file mode 100644 index 00000000..93aca75e --- /dev/null +++ b/application/api/controller/BackgroundCheck.php @@ -0,0 +1,386 @@ +service = new BackgroundCheckService(); + } + + // ===================== 公开 API ===================== + + /** + * Demo:快速体验 + * + * @param string orcid 可选,默认 0000-0002-2582-7480 + */ + public function demo() + { + $orcid = $this->service->cleanOrcid($this->request->param('orcid', '0000-0002-2582-7480')); + + $report = $this->buildProfileReport([ + 'orcid' => $orcid, + 'with_crossref_detail'=> 1, + ]); + if (!$report['success']) { + return jsonError($report['error']); + } + + $report['data']['_demo_note'] = 'Demo 接口,正式使用请调用 checkProfile 或 batchScreenByField'; + return jsonSuccess($report['data']); + } + + /** + * 按姓名搜索学者 + * + * @param string name 学者姓名(必填) + * @param string affiliation 单位关键词(可选) + * @param int limit 返回条数,默认5,最大20 + */ + public function searchAuthor() + { + $name = trim($this->request->param('name', '')); + if ($name === '') { + return jsonError('name不能为空'); + } + + $affiliation = trim($this->request->param('affiliation', '')); + $limit = min(max(intval($this->request->param('limit', 5)), 1), 20); + + $cacheKey = 'bg_search_' . md5($name . $affiliation . $limit); + $cached = Cache::get($cacheKey); + if ($cached) { + return jsonSuccess($cached); + } + + $filter = 'display_name.search:' . $name; + if ($affiliation !== '') { + $filter .= ',last_known_institutions.display_name.search:' . $affiliation; + } + + $res = $this->service->openAlexGet('/authors', [ + 'search' => $name, + 'filter' => $filter, + 'sort' => 'cited_by_count:desc', + 'per-page' => $limit, + ]); + + if (!$res['success']) { + return jsonError($res['error']); + } + + $list = []; + foreach ($res['data']['results'] ?? [] as $author) { + $list[] = $this->service->formatAuthorBrief($author); + } + + $result = [ + 'query' => ['name' => $name, 'affiliation' => $affiliation], + 'total' => $res['data']['meta']['count'] ?? count($list), + 'list' => $list, + ]; + + Cache::set($cacheKey, $result, 1800); + return jsonSuccess($result); + } + + /** + * 完整背景调查报告 + * + * @param string openalex_id OpenAlex 作者ID + * @param string orcid ORCID + * @param string name 姓名 + * @param string affiliation 单位关键词 + * @param int user_id 本地用户ID(可选) + * @param int with_crossref_detail 是否补充 CrossRef 撤稿详情,默认1 + */ + public function checkProfile() + { + $params = [ + 'openalex_id' => trim($this->request->param('openalex_id', '')), + 'orcid' => $this->service->cleanOrcid($this->request->param('orcid', '')), + 'name' => trim($this->request->param('name', '')), + 'affiliation' => trim($this->request->param('affiliation', '')), + 'user_id' => intval($this->request->param('user_id', 0)), + 'with_crossref_detail' => intval($this->request->param('with_crossref_detail', 1)), + ]; + + if ($params['openalex_id'] === '' && $params['orcid'] === '' && $params['name'] === '') { + return jsonError('请至少提供 openalex_id、orcid 或 name 之一'); + } + + $report = $this->buildProfileReport($params); + if (!$report['success']) { + return jsonError($report['error']); + } + + return jsonSuccess($report['data']); + } + + /** + * 单篇 DOI 撤稿详情(CrossRef + Retraction Watch 来源标记) + * + * @param string doi 文章 DOI(必填) + */ + public function checkRetractionByDoi() + { + $doi = trim($this->request->param('doi', '')); + if ($doi === '') { + return jsonError('doi不能为空'); + } + + $res = $this->service->fetchCrossRefWork($doi); + if (!$res['success']) { + return jsonError($res['error']); + } + + $detail = $this->service->parseCrossRefRetractionDetail($doi, $res['message']); + $sources = $detail['retraction_detail']['sources'] ?? []; + $fromRw = false; + foreach ($sources as $src) { + if (stripos($src, 'retraction-watch') !== false || stripos($src, 'retraction_watch') !== false) { + $fromRw = true; + break; + } + } + + return jsonSuccess([ + 'doi' => $detail['doi'], + 'title' => $detail['title'], + 'journal' => $detail['journal'], + 'publisher' => $detail['publisher'], + 'authors' => $detail['authors'], + 'is_retracted' => $detail['is_retracted'], + 'from_retraction_watch' => $fromRw || !empty($detail['retraction_detail']['record_ids']), + 'retraction_detail' => $detail['retraction_detail'], + 'url' => $detail['url'], + 'data_sources' => ['CrossRef', 'Retraction Watch'], + ]); + } + + /** + * 按领域批量筛查专家(含撤稿风险标签) + * + * @param string keyword 领域关键词(必填),如 immunotherapy、cancer + * @param int min_h_index 最低 h-index,默认5 + * @param int limit 每页数量,默认10,最大30 + * @param int page 页码,默认1 + * @param int check_retraction 是否检查撤稿,默认1 + */ + public function batchScreenByField() + { + $keyword = trim($this->request->param('keyword', '')); + if ($keyword === '') { + return jsonError('keyword不能为空'); + } + + $options = [ + 'min_h_index' => intval($this->request->param('min_h_index', 5)), + 'limit' => min(max(intval($this->request->param('limit', 10)), 1), 30), + 'page' => max(intval($this->request->param('page', 1)), 1), + ]; + $checkRetraction = intval($this->request->param('check_retraction', 1)); + + $cacheKey = 'bg_batch_' . md5(json_encode([$keyword, $options, $checkRetraction])); + $cached = Cache::get($cacheKey); + if ($cached) { + return jsonSuccess($cached); + } + + $searchRes = $this->service->searchAuthorsByField($keyword, $options); + if (!$searchRes['success']) { + return jsonError($searchRes['error']); + } + + $data = $searchRes['data']; + $screened = []; + $riskStats = ['low' => 0, 'medium' => 0, 'high' => 0]; + + foreach ($data['list'] as $author) { + $item = [ + 'profile' => $author, + 'metrics' => [ + 'works_count' => $author['works_count'], + 'cited_by_count' => $author['cited_by_count'], + 'h_index' => $author['h_index'], + 'level_label' => $this->service->parseAuthorMetrics([ + 'works_count' => $author['works_count'], + 'cited_by_count' => $author['cited_by_count'], + 'summary_stats' => ['h_index' => $author['h_index']], + ])['level_label'], + ], + ]; + + if ($checkRetraction) { + $openAlexId = $author['openalex_id']; + $oaRetractions = $this->service->fetchRetractedWorksOpenAlex($openAlexId); + $rwRetractions = $this->service->fetchRetractionWatchByAuthor($author['name']); + $merged = $this->service->mergeRetractionRecords($oaRetractions, $rwRetractions, false); + + $metrics = ['works_count' => $author['works_count']]; + $risk = $this->service->assessRisk($metrics, $merged); + + $item['retractions'] = [ + 'count' => $merged['count'], + 'openalex_count'=> $merged['openalex_count'], + 'rw_count' => $merged['rw_count'], + 'rw_only_count' => $merged['rw_only_count'], + 'list' => array_slice($merged['list'], 0, 3), + ]; + $item['risk_assessment'] = $risk; + $riskStats[$risk['level']]++; + } else { + $item['risk_assessment'] = [ + 'level' => 'unknown', + 'level_label' => '未检测', + ]; + } + + $screened[] = $item; + usleep(300000); + } + + $result = [ + 'query' => [ + 'keyword' => $keyword, + 'topic_id' => $data['topic_id'], + 'min_h_index' => $options['min_h_index'], + 'page' => $options['page'], + 'limit' => $options['limit'], + 'check_retraction' => $checkRetraction, + ], + 'total' => $data['total'], + 'risk_stats' => $checkRetraction ? $riskStats : null, + 'list' => $screened, + 'disclaimer' => '批量筛查基于公开数据,同名作者可能存在误匹配,高风险条目需人工复核。', + ]; + + Cache::set($cacheKey, $result, 900); + return jsonSuccess($result); + } + + // ===================== 核心逻辑 ===================== + + /** + * 构建完整背景调查报告 + */ + private function buildProfileReport($params) + { + $withCrossRef = !empty($params['with_crossref_detail']); + + $author = $this->service->resolveAuthor($params); + if (!$author['success']) { + return $author; + } + + $authorData = $author['data']; + $openAlexId = $this->service->extractOpenAlexId($authorData['id']); + $authorName = $authorData['display_name'] ?? ''; + + $metrics = $this->service->parseAuthorMetrics($authorData); + $topics = $this->service->parseResearchTopics($authorData); + $recentWorks = $this->service->fetchRecentWorks($openAlexId, 5); + + // 扩展1: OpenAlex 撤稿 + $oaRetractions = $this->service->fetchRetractedWorksOpenAlex($openAlexId); + + // 扩展1: Retraction Watch 二次核对(CrossRef 来源) + $rwRetractions = $this->service->fetchRetractionWatchByAuthor($authorName); + + // 扩展2: 合并并按需补充 CrossRef 撤稿详情 + $retractions = $this->service->mergeRetractionRecords( + $oaRetractions, + $rwRetractions, + $withCrossRef + ); + + $risk = $this->service->assessRisk($metrics, $retractions); + + $localInfo = null; + if (!empty($params['user_id'])) { + $localInfo = $this->fetchLocalUserInfo($params['user_id']); + } + + $dataSources = ['OpenAlex', 'Retraction Watch (via CrossRef)']; + if ($withCrossRef) { + $dataSources[] = 'CrossRef'; + } + + return [ + 'success' => true, + 'data' => [ + 'profile' => $this->service->formatAuthorBrief($authorData), + 'metrics' => $metrics, + 'research_topics' => $topics, + 'recent_works' => $recentWorks, + 'retractions' => $retractions, + 'risk_assessment' => $risk, + 'local_info' => $localInfo, + 'data_sources' => $dataSources, + 'disclaimer' => '本报告仅基于公开学术数据,不能替代正式背调或机构调查;未公开的不端行为无法检测。', + 'generated_at' => date('Y-m-d H:i:s'), + ], + ]; + } + + /** + * 合并本地用户信息 + */ + private function fetchLocalUserInfo($userId) + { + $user = $this->user_obj->where('user_id', $userId)->find(); + if (!$user) { + return null; + } + + $reviewer = $this->user_reviewer_info_obj + ->where('reviewer_id', $userId) + ->where('state', 0) + ->find(); + + $majors = $this->major_to_user_obj + ->where('user_id', $userId) + ->where('state', 0) + ->select(); + + $majorList = []; + foreach ($majors as $m) { + $majorList[] = [ + 'major_id' => $m['major_id'], + 'path' => getMajorStr($m['major_id']), + ]; + } + + return [ + 'user_id' => $userId, + 'realname' => $user['realname'] ?? '', + 'email' => $user['email'] ?? '', + 'orcid' => $user['orcid'] ?? '', + 'company' => $reviewer['company'] ?? '', + 'title' => $reviewer['technical'] ?? '', + 'field' => $reviewer['field'] ?? '', + 'majors' => $majorList, + ]; + } +} diff --git a/application/common/BackgroundCheckService.php b/application/common/BackgroundCheckService.php new file mode 100644 index 00000000..78ca5426 --- /dev/null +++ b/application/common/BackgroundCheckService.php @@ -0,0 +1,705 @@ +mailto; + $url = $this->openAlexBase . $path . '?' . http_build_query($query); + + $result = $this->httpGet($url, [ + 'Accept: application/json', + 'User-Agent: TMRJournals-BackgroundCheck/1.0 (mailto:' . $this->mailto . ')', + ]); + + if (!$result['success']) { + return $result; + } + + $data = json_decode($result['body'], true); + if (!is_array($data)) { + return ['success' => false, 'error' => 'OpenAlex返回数据格式异常']; + } + + return ['success' => true, 'data' => $data]; + } + + public function resolveAuthor($params) + { + if (!empty($params['openalex_id'])) { + $id = preg_replace('/^https?:\/\/openalex\.org\//', '', $params['openalex_id']); + $res = $this->openAlexGet('/authors/' . urlencode($id)); + if (!$res['success']) { + return ['success' => false, 'error' => $res['error']]; + } + return ['success' => true, 'data' => $res['data']]; + } + + if (!empty($params['orcid'])) { + $orcid = $this->cleanOrcid($params['orcid']); + $res = $this->openAlexGet('/authors/https://orcid.org/' . $orcid); + if (!$res['success']) { + return ['success' => false, 'error' => '未在 OpenAlex 找到该 ORCID 对应学者']; + } + return ['success' => true, 'data' => $res['data']]; + } + + if (empty($params['name'])) { + return ['success' => false, 'error' => '请提供 openalex_id、orcid 或 name']; + } + + $filter = 'display_name.search:' . $params['name']; + if (!empty($params['affiliation'])) { + $filter .= ',last_known_institutions.display_name.search:' . $params['affiliation']; + } + + $res = $this->openAlexGet('/authors', [ + 'search' => $params['name'], + 'filter' => $filter, + 'sort' => 'cited_by_count:desc', + 'per-page' => 1, + ]); + + if (!$res['success']) { + return ['success' => false, 'error' => $res['error']]; + } + + $results = $res['data']['results'] ?? []; + if (empty($results)) { + return ['success' => false, 'error' => '未找到匹配学者,请补充 affiliation 或使用 orcid']; + } + + return ['success' => true, 'data' => $results[0]]; + } + + public function fetchRetractedWorksOpenAlex($openAlexId) + { + $res = $this->openAlexGet('/works', [ + 'filter' => 'authorships.author.id:' . $openAlexId . ',is_retracted:true', + 'sort' => 'publication_date:desc', + 'per-page' => 25, + ]); + + if (!$res['success']) { + return ['count' => 0, 'list' => [], 'error' => $res['error']]; + } + + $list = []; + foreach ($res['data']['results'] ?? [] as $work) { + $list[] = $this->formatOpenAlexWork($work); + } + + return ['count' => count($list), 'list' => $list, 'source' => 'openalex']; + } + + public function fetchRecentWorks($openAlexId, $limit = 5) + { + $res = $this->openAlexGet('/works', [ + 'filter' => 'authorships.author.id:' . $openAlexId, + 'sort' => 'publication_date:desc', + 'per-page' => $limit, + ]); + + if (!$res['success']) { + return []; + } + + $list = []; + foreach ($res['data']['results'] ?? [] as $work) { + $item = $this->formatOpenAlexWork($work); + $item['is_retracted'] = !empty($work['is_retracted']); + $list[] = $item; + } + + return $list; + } + + /** + * 按领域/关键词批量搜索学者(OpenAlex) + */ + public function searchAuthorsByField($keyword, $options = []) + { + $minHIndex = intval($options['min_h_index'] ?? 5); + $limit = min(max(intval($options['limit'] ?? 10), 1), 30); + $page = max(intval($options['page'] ?? 1), 1); + + $topicId = $this->resolveTopicId($keyword); + $filters = []; + + if ($topicId !== '') { + $filters[] = 'topics.id:' . $topicId; + } + if ($minHIndex > 0) { + $filters[] = 'summary_stats.h_index:>' . $minHIndex; + } + + $query = [ + 'sort' => 'cited_by_count:desc', + 'per-page' => $limit, + 'page' => $page, + ]; + + if (!empty($filters)) { + $query['filter'] = implode(',', $filters); + $query['search'] = $keyword; + } else { + $query['search'] = $keyword; + } + + $res = $this->openAlexGet('/authors', $query); + if (!$res['success']) { + return ['success' => false, 'error' => $res['error']]; + } + + $authors = []; + foreach ($res['data']['results'] ?? [] as $author) { + $authors[] = $this->formatAuthorBrief($author); + } + + return [ + 'success' => true, + 'data' => [ + 'keyword' => $keyword, + 'topic_id' => $topicId, + 'page' => $page, + 'limit' => $limit, + 'total' => $res['data']['meta']['count'] ?? count($authors), + 'list' => $authors, + ], + ]; + } + + private function resolveTopicId($keyword) + { + $res = $this->openAlexGet('/topics', [ + 'search' => $keyword, + 'sort' => 'works_count:desc', + 'per-page' => 1, + ]); + + if (!$res['success']) { + return ''; + } + + $results = $res['data']['results'] ?? []; + if (empty($results)) { + return ''; + } + + return $this->extractOpenAlexId($results[0]['id'] ?? ''); + } + + // ===================== CrossRef ===================== + + public function cleanDoi($doi) + { + $doi = trim($doi); + $doi = preg_replace('/^https?:\/\/doi\.org\//', '', $doi); + $doi = preg_replace('/^doi:\s*/i', '', $doi); + return trim($doi); + } + + public function fetchCrossRefWork($doi) + { + $doi = $this->cleanDoi($doi); + if ($doi === '') { + return ['success' => false, 'error' => 'DOI为空']; + } + + $url = $this->crossRefBase . '/works/' . urlencode($doi); + $result = $this->httpGet($url, [ + 'Accept: application/json', + 'User-Agent: TMRJournals-BackgroundCheck/1.0 (mailto:' . $this->mailto . ')', + ]); + + if (!$result['success']) { + return ['success' => false, 'error' => $result['error']]; + } + + if ($result['http_code'] == 404) { + return ['success' => false, 'error' => 'DOI在CrossRef中未找到']; + } + if ($result['http_code'] != 200) { + return ['success' => false, 'error' => 'CrossRef返回 HTTP ' . $result['http_code']]; + } + + $data = json_decode($result['body'], true); + if (!isset($data['message'])) { + return ['success' => false, 'error' => 'CrossRef返回数据格式异常']; + } + + return ['success' => true, 'message' => $data['message']]; + } + + public function parseCrossRefRetractionDetail($doi, $message) + { + $retraction = $this->detectCrossRefRetraction($message); + + return [ + 'doi' => $this->cleanDoi($doi), + 'title' => isset($message['title'][0]) ? $message['title'][0] : '', + 'is_retracted' => $retraction['is_retracted'], + 'retraction_detail' => $retraction['retraction_detail'], + 'journal' => isset($message['container-title'][0]) ? $message['container-title'][0] : '', + 'publisher' => $message['publisher'] ?? '', + 'published_date' => isset($message['published-print']) ? $this->parseDateParts($message['published-print']) : '', + 'authors' => $this->parseCrossRefAuthors($message['author'] ?? []), + 'url' => $message['URL'] ?? ('https://doi.org/' . $this->cleanDoi($doi)), + ]; + } + + public function enrichRetractionsWithCrossRef($retractionList) + { + $enriched = []; + foreach ($retractionList as $item) { + $doi = $this->cleanDoi($item['doi'] ?? ''); + if ($doi === '') { + $item['crossref'] = ['success' => false, 'error' => '无DOI']; + $enriched[] = $item; + continue; + } + + $res = $this->fetchCrossRefWork($doi); + if (!$res['success']) { + $item['crossref'] = ['success' => false, 'error' => $res['error']]; + } else { + $item['crossref'] = [ + 'success' => true, + 'data' => $this->parseCrossRefRetractionDetail($doi, $res['message']), + ]; + } + + $enriched[] = $item; + usleep(200000); + } + + return $enriched; + } + + private function detectCrossRefRetraction($message) + { + $isRetracted = false; + $retractionDetail = [ + 'sources' => [], + 'retraction_notices' => [], + 'record_ids' => [], + ]; + + foreach (['updated-by', 'update-to'] as $field) { + if (!isset($message[$field]) || !is_array($message[$field])) { + continue; + } + foreach ($message[$field] as $update) { + $updateType = strtolower($update['type'] ?? ''); + $updateLabel = strtolower($update['label'] ?? ''); + if (strpos($updateType, 'retract') === false && strpos($updateLabel, 'retract') === false) { + continue; + } + + $isRetracted = true; + $source = $update['source'] ?? 'publisher'; + $retractionDetail['sources'][] = $source; + + $notice = [ + 'type' => $update['type'] ?? '', + 'label' => $update['label'] ?? '', + 'source' => $source, + 'notice_doi'=> $update['DOI'] ?? '', + 'date' => isset($update['updated']) ? $this->parseDateParts($update['updated']) : '', + 'record_id' => $update['record-id'] ?? '', + ]; + $retractionDetail['retraction_notices'][] = $notice; + + if (!empty($notice['record_id'])) { + $retractionDetail['record_ids'][] = $notice['record_id']; + } + } + } + + $type = strtolower($message['type'] ?? ''); + $subtype = strtolower($message['subtype'] ?? ''); + if (strpos($type, 'retract') !== false || strpos($subtype, 'retract') !== false) { + $isRetracted = true; + $retractionDetail['is_retraction_notice'] = true; + } + + if (isset($message['relation']) && is_array($message['relation'])) { + foreach ($message['relation'] as $relType => $relations) { + if (strpos(strtolower($relType), 'retract') !== false) { + $isRetracted = true; + $retractionDetail['relation'] = [$relType => $relations]; + break; + } + } + } + + $retractionDetail['sources'] = array_values(array_unique($retractionDetail['sources'])); + $retractionDetail['record_ids'] = array_values(array_unique($retractionDetail['record_ids'])); + + return ['is_retracted' => $isRetracted, 'retraction_detail' => $retractionDetail]; + } + + // ===================== Retraction Watch (via CrossRef) ===================== + + /** + * 通过 CrossRef 检索 Retraction Watch 来源的撤稿记录(按作者姓名) + */ + public function fetchRetractionWatchByAuthor($authorName) + { + $url = $this->crossRefBase . '/works?' . http_build_query([ + 'query.author' => $authorName, + 'filter' => 'update-type:retraction', + 'rows' => 25, + 'mailto' => $this->mailto, + ]); + + $result = $this->httpGet($url, [ + 'Accept: application/json', + 'User-Agent: TMRJournals-BackgroundCheck/1.0 (mailto:' . $this->mailto . ')', + ]); + + if (!$result['success']) { + return ['count' => 0, 'list' => [], 'error' => $result['error']]; + } + + if ($result['http_code'] != 200) { + return ['count' => 0, 'list' => [], 'error' => 'CrossRef返回 HTTP ' . $result['http_code']]; + } + + $data = json_decode($result['body'], true); + $items = $data['message']['items'] ?? []; + + $list = []; + foreach ($items as $message) { + $parsed = $this->parseCrossRefRetractionDetail($message['DOI'] ?? '', $message); + if (!$parsed['is_retracted']) { + continue; + } + + $rwSources = array_filter($parsed['retraction_detail']['sources'] ?? [], function ($s) { + return stripos($s, 'retraction-watch') !== false || stripos($s, 'retraction_watch') !== false; + }); + + $list[] = [ + 'title' => $parsed['title'], + 'doi' => $parsed['doi'], + 'journal' => $parsed['journal'], + 'publisher' => $parsed['publisher'], + 'published_date' => $parsed['published_date'], + 'is_retracted' => true, + 'retraction_detail' => $parsed['retraction_detail'], + 'from_retraction_watch' => !empty($rwSources) || !empty($parsed['retraction_detail']['record_ids']), + 'source' => 'retraction_watch', + ]; + } + + return [ + 'count' => count($list), + 'list' => $list, + 'source' => 'retraction_watch', + ]; + } + + /** + * 合并 OpenAlex + Retraction Watch 撤稿记录(按 DOI 去重) + */ + public function mergeRetractionRecords($openAlexRetractions, $rwRetractions, $withCrossRefDetail = false) + { + $merged = []; + $doiMap = []; + + foreach ([$openAlexRetractions, $rwRetractions] as $sourceData) { + foreach ($sourceData['list'] ?? [] as $item) { + $doi = $this->cleanDoi($item['doi'] ?? ''); + $key = $doi !== '' ? strtolower($doi) : md5(json_encode($item)); + + if (!isset($doiMap[$key])) { + $doiMap[$key] = [ + 'title' => $item['title'] ?? '', + 'doi' => $doi, + 'journal' => $item['journal'] ?? '', + 'publication_date' => $item['publication_date'] ?? ($item['published_date'] ?? ''), + 'sources' => [], + 'retraction_detail'=> $item['retraction_detail'] ?? [], + 'from_retraction_watch' => !empty($item['from_retraction_watch']), + ]; + } + + $src = $item['source'] ?? 'unknown'; + if (!in_array($src, $doiMap[$key]['sources'])) { + $doiMap[$key]['sources'][] = $src; + } + if (!empty($item['from_retraction_watch'])) { + $doiMap[$key]['from_retraction_watch'] = true; + } + if (!empty($item['retraction_detail']) && empty($doiMap[$key]['retraction_detail'])) { + $doiMap[$key]['retraction_detail'] = $item['retraction_detail']; + } + } + } + + $merged = array_values($doiMap); + + if ($withCrossRefDetail) { + $merged = $this->enrichRetractionsWithCrossRef($merged); + } + + $rwOnlyCount = 0; + foreach ($merged as $row) { + if (!empty($row['from_retraction_watch']) && count($row['sources'] ?? []) <= 1) { + $rwOnlyCount++; + } + } + + return [ + 'count' => count($merged), + 'openalex_count' => intval($openAlexRetractions['count'] ?? 0), + 'rw_count' => intval($rwRetractions['count'] ?? 0), + 'rw_only_count' => $rwOnlyCount, + 'list' => $merged, + ]; + } + + // ===================== 格式化 ===================== + + public function formatAuthorBrief($author) + { + $institutions = []; + foreach ($author['last_known_institutions'] ?? [] as $inst) { + $institutions[] = [ + 'name' => $inst['display_name'] ?? '', + 'country' => $inst['country_code'] ?? '', + ]; + } + + return [ + 'openalex_id' => $this->extractOpenAlexId($author['id'] ?? ''), + 'name' => $author['display_name'] ?? '', + 'orcid' => $this->extractOrcid($author['orcid'] ?? ''), + 'works_count' => intval($author['works_count'] ?? 0), + 'cited_by_count' => intval($author['cited_by_count'] ?? 0), + 'h_index' => intval($author['summary_stats']['h_index'] ?? 0), + 'institutions' => $institutions, + 'openalex_url' => $author['id'] ?? '', + ]; + } + + public function parseAuthorMetrics($author) + { + $stats = $author['summary_stats'] ?? []; + + return [ + 'works_count' => intval($author['works_count'] ?? 0), + 'cited_by_count' => intval($author['cited_by_count'] ?? 0), + 'h_index' => intval($stats['h_index'] ?? 0), + 'i10_index' => intval($stats['i10_index'] ?? 0), + 'two_year_mean_cited' => round(floatval($stats['2yr_mean_citedness'] ?? 0), 2), + 'level_label' => $this->getAcademicLevelLabel($stats), + ]; + } + + public function parseResearchTopics($author) + { + $topics = []; + foreach ($author['x_concepts'] ?? [] as $concept) { + if (empty($concept['display_name'])) { + continue; + } + $topics[] = [ + 'name' => $concept['display_name'], + 'score' => round(floatval($concept['score'] ?? 0), 3), + ]; + } + + if (empty($topics)) { + foreach ($author['topics'] ?? [] as $topic) { + if (empty($topic['display_name'])) { + continue; + } + $topics[] = [ + 'name' => $topic['display_name'], + 'score' => round(floatval($topic['score'] ?? 0), 3), + ]; + } + } + + return array_slice($topics, 0, 8); + } + + public function assessRisk($metrics, $retractions) + { + $retractionCount = intval($retractions['count'] ?? 0); + $rwOnlyCount = intval($retractions['rw_only_count'] ?? 0); + $level = 'low'; + $score = 0; + $reasons = []; + + if ($retractionCount === 0) { + $level = 'low'; + $score = 10; + $reasons[] = 'OpenAlex 与 Retraction Watch 均未发现撤稿记录'; + } elseif ($retractionCount === 1) { + $level = 'medium'; + $score = 50; + $reasons[] = '发现 1 篇撤稿论文,建议人工核实撤稿原因'; + } else { + $level = 'high'; + $score = 80 + min($retractionCount * 5, 20); + $reasons[] = '发现 ' . $retractionCount . ' 篇撤稿论文,存在较高学术风险'; + } + + if ($rwOnlyCount > 0) { + $reasons[] = 'Retraction Watch 额外发现 ' . $rwOnlyCount . ' 条 OpenAlex 未收录的撤稿记录'; + if ($level === 'low') { + $level = 'medium'; + $score = max($score, 45); + } + } + + $worksCount = max(intval($metrics['works_count'] ?? 0), 1); + $retractionRate = round($retractionCount / $worksCount * 100, 2); + if ($retractionCount > 0 && $retractionRate >= 5) { + $reasons[] = '撤稿率 ' . $retractionRate . '%,比例偏高'; + if ($level === 'medium') { + $level = 'high'; + $score = max($score, 70); + } + } + + return [ + 'level' => $level, + 'level_label' => $this->getRiskLevelLabel($level), + 'score' => min($score, 100), + 'retraction_count' => $retractionCount, + 'retraction_rate' => $retractionRate . '%', + 'rw_only_count' => $rwOnlyCount, + 'reasons' => $reasons, + ]; + } + + // ===================== 内部工具 ===================== + + private function formatOpenAlexWork($work) + { + return [ + 'title' => $work['display_name'] ?? '', + 'doi' => $this->extractDoi($work), + 'publication_date' => $work['publication_date'] ?? '', + 'journal' => $work['primary_location']['source']['display_name'] ?? '', + 'cited_by_count' => intval($work['cited_by_count'] ?? 0), + 'openalex_url' => $work['id'] ?? '', + 'source' => 'openalex', + ]; + } + + private function parseCrossRefAuthors($authorList) + { + if (empty($authorList) || !is_array($authorList)) { + return []; + } + + $result = []; + foreach ($authorList as $a) { + $result[] = [ + 'given' => $a['given'] ?? '', + 'family' => $a['family'] ?? '', + 'name' => isset($a['name']) ? $a['name'] : trim(($a['given'] ?? '') . ' ' . ($a['family'] ?? '')), + 'orcid' => $a['ORCID'] ?? '', + ]; + } + return $result; + } + + private function parseDateParts($dateObj) + { + if (!isset($dateObj['date-parts'][0])) { + return ''; + } + $parts = $dateObj['date-parts'][0]; + $y = isset($parts[0]) ? $parts[0] : ''; + $m = isset($parts[1]) ? sprintf('%02d', $parts[1]) : ''; + $d = isset($parts[2]) ? sprintf('%02d', $parts[2]) : ''; + if ($y && $m && $d) { + return "{$y}-{$m}-{$d}"; + } + if ($y && $m) { + return "{$y}-{$m}"; + } + return (string)$y; + } + + private function getAcademicLevelLabel($stats) + { + $h = intval($stats['h_index'] ?? 0); + if ($h >= 50) return '国际顶尖学者'; + if ($h >= 30) return '资深专家'; + if ($h >= 15) return '活跃研究者'; + if ($h >= 5) return '青年学者'; + if ($h > 0) return '初入领域'; + return '暂无足够公开数据'; + } + + private function getRiskLevelLabel($level) + { + $map = ['low' => '低风险', 'medium' => '中风险', 'high' => '高风险']; + return $map[$level] ?? '未知'; + } + + public function extractOpenAlexId($id) + { + return preg_replace('/^https?:\/\/openalex\.org\//', '', $id); + } + + public function extractOrcid($orcid) + { + if ($orcid === '') return ''; + return preg_replace('/^https?:\/\/orcid\.org\//', '', $orcid); + } + + public function cleanOrcid($orcid) + { + $orcid = trim($orcid); + $orcid = preg_replace('/^https?:\/\/orcid\.org\//', '', $orcid); + return trim($orcid); + } + + private function extractDoi($work) + { + $doi = $work['doi'] ?? ''; + return preg_replace('/^https?:\/\/doi\.org\//', '', $doi); + } + + private function httpGet($url, $headers = []) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + $body = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if (curl_errno($ch)) { + $error = curl_error($ch); + curl_close($ch); + return ['success' => false, 'error' => 'HTTP请求失败: ' . $error]; + } + curl_close($ch); + + return ['success' => true, 'body' => $body, 'http_code' => $httpCode]; + } +} diff --git a/composer.json b/composer.json index cd86d23c..a0a034eb 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,10 @@ "topthink/think-installer": true, "php-http/discovery": true }, - "secure-http": false + "secure-http": false, + "audit": { + "block-insecure": false + } }, "repositories": [{ "name": "aliyun", diff --git a/vendor/autoload.php b/vendor/autoload.php index 9eaf68c9..7f041de1 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) { echo $err; } } - trigger_error( - $err, - E_USER_ERROR - ); + throw new RuntimeException($err); } require_once __DIR__ . '/composer/autoload_real.php'; diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 730bac18..0fb0a2c1 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,10 +6,5 @@ $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( - 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', - 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', - 'Stringable' => $vendorDir . '/myclabs/php-enum/stubs/Stringable.php', - 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', - 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', ); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 8c79ad6e..5db964a2 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -7,12 +7,12 @@ $baseDir = dirname($vendorDir); return array( '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', - '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', - 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '9c67151ae59aff4788964ce8eb2a0f43' => $vendorDir . '/clue/stream-filter/src/functions_include.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', '8cff32064859f4559445b89279f3199c' => $vendorDir . '/php-http/message/src/filters.php', + 'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php', + '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', '9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 6db206ef..6ce0181d 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -15,12 +15,11 @@ return array( 'ZipStream\\' => array($vendorDir . '/maennchen/zipstream-php/src'), 'Unirest\\' => array($vendorDir . '/apimatic/unirest-php/src'), 'Tectalic\\OpenAi\\' => array($vendorDir . '/tectalic/openai/src'), - 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), 'Spatie\\DataTransferObject\\' => array($vendorDir . '/spatie/data-transfer-object/src'), 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), - 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/src'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), 'PhpOffice\\PhpWord\\' => array($vendorDir . '/phpoffice/phpword/src/PhpWord'), @@ -31,7 +30,6 @@ return array( 'ParagonIE\\ConstantTime\\' => array($vendorDir . '/paragonie/constant_time_encoding/src'), 'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'), 'Nyholm\\Psr7\\' => array($vendorDir . '/nyholm/psr7/src'), - 'MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'), 'Matrix\\' => array($vendorDir . '/markbaker/matrix/classes/src'), 'Http\\Message\\MultipartStream\\' => array($vendorDir . '/php-http/multipart-stream-builder/src'), 'Http\\Message\\' => array($vendorDir . '/php-http/message/src'), @@ -41,6 +39,7 @@ return array( 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), 'Core\\' => array($vendorDir . '/apimatic/core/src'), 'CoreInterfaces\\' => array($vendorDir . '/apimatic/core-interfaces/src'), + 'Composer\\Pcre\\' => array($vendorDir . '/composer/pcre/src'), 'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'), 'Clue\\StreamFilter\\' => array($vendorDir . '/clue/stream-filter/src'), ); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 11d4dcbc..e9626643 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit7020b987d316c2076c2a6f439a1140f9 +class ComposerAutoloaderInit2bc4f313dba415539e266f7ac2c87dcd { private static $loader; @@ -22,18 +22,16 @@ class ComposerAutoloaderInit7020b987d316c2076c2a6f439a1140f9 return self::$loader; } - require __DIR__ . '/platform_check.php'; - - spl_autoload_register(array('ComposerAutoloaderInit7020b987d316c2076c2a6f439a1140f9', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit2bc4f313dba415539e266f7ac2c87dcd', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInit7020b987d316c2076c2a6f439a1140f9', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit2bc4f313dba415539e266f7ac2c87dcd', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit7020b987d316c2076c2a6f439a1140f9::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit2bc4f313dba415539e266f7ac2c87dcd::getInitializer($loader)); $loader->register(true); - $filesToLoad = \Composer\Autoload\ComposerStaticInit7020b987d316c2076c2a6f439a1140f9::$files; + $filesToLoad = \Composer\Autoload\ComposerStaticInit2bc4f313dba415539e266f7ac2c87dcd::$files; $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index fc6d53f6..38ba10b9 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,17 +4,16 @@ namespace Composer\Autoload; -class ComposerStaticInit7020b987d316c2076c2a6f439a1140f9 +class ComposerStaticInit2bc4f313dba415539e266f7ac2c87dcd { public static $files = array ( '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', - '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', - 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '9c67151ae59aff4788964ce8eb2a0f43' => __DIR__ . '/..' . '/clue/stream-filter/src/functions_include.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', '8cff32064859f4559445b89279f3199c' => __DIR__ . '/..' . '/php-http/message/src/filters.php', 'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php', + '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', '9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php', @@ -51,7 +50,6 @@ class ComposerStaticInit7020b987d316c2076c2a6f439a1140f9 ), 'S' => array ( - 'Symfony\\Polyfill\\Php80\\' => 23, 'Symfony\\Polyfill\\Mbstring\\' => 26, 'Symfony\\Component\\HttpFoundation\\' => 33, 'Spatie\\DataTransferObject\\' => 26, @@ -76,7 +74,6 @@ class ComposerStaticInit7020b987d316c2076c2a6f439a1140f9 ), 'M' => array ( - 'MyCLabs\\Enum\\' => 13, 'Matrix\\' => 7, ), 'H' => @@ -95,6 +92,7 @@ class ComposerStaticInit7020b987d316c2076c2a6f439a1140f9 array ( 'Core\\' => 5, 'CoreInterfaces\\' => 15, + 'Composer\\Pcre\\' => 14, 'Complex\\' => 8, 'Clue\\StreamFilter\\' => 18, ), @@ -140,10 +138,6 @@ class ComposerStaticInit7020b987d316c2076c2a6f439a1140f9 array ( 0 => __DIR__ . '/..' . '/tectalic/openai/src', ), - 'Symfony\\Polyfill\\Php80\\' => - array ( - 0 => __DIR__ . '/..' . '/symfony/polyfill-php80', - ), 'Symfony\\Polyfill\\Mbstring\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', @@ -162,7 +156,7 @@ class ComposerStaticInit7020b987d316c2076c2a6f439a1140f9 ), 'Psr\\Log\\' => array ( - 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + 0 => __DIR__ . '/..' . '/psr/log/src', ), 'Psr\\Http\\Message\\' => array ( @@ -205,10 +199,6 @@ class ComposerStaticInit7020b987d316c2076c2a6f439a1140f9 array ( 0 => __DIR__ . '/..' . '/nyholm/psr7/src', ), - 'MyCLabs\\Enum\\' => - array ( - 0 => __DIR__ . '/..' . '/myclabs/php-enum/src', - ), 'Matrix\\' => array ( 0 => __DIR__ . '/..' . '/markbaker/matrix/classes/src', @@ -245,6 +235,10 @@ class ComposerStaticInit7020b987d316c2076c2a6f439a1140f9 array ( 0 => __DIR__ . '/..' . '/apimatic/core-interfaces/src', ), + 'Composer\\Pcre\\' => + array ( + 0 => __DIR__ . '/..' . '/composer/pcre/src', + ), 'Complex\\' => array ( 0 => __DIR__ . '/..' . '/markbaker/complex/classes/src', @@ -280,21 +274,16 @@ class ComposerStaticInit7020b987d316c2076c2a6f439a1140f9 ); public static $classMap = array ( - 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', - 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', - 'Stringable' => __DIR__ . '/..' . '/myclabs/php-enum/stubs/Stringable.php', - 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', - 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit7020b987d316c2076c2a6f439a1140f9::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit7020b987d316c2076c2a6f439a1140f9::$prefixDirsPsr4; - $loader->prefixesPsr0 = ComposerStaticInit7020b987d316c2076c2a6f439a1140f9::$prefixesPsr0; - $loader->classMap = ComposerStaticInit7020b987d316c2076c2a6f439a1140f9::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit2bc4f313dba415539e266f7ac2c87dcd::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit2bc4f313dba415539e266f7ac2c87dcd::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit2bc4f313dba415539e266f7ac2c87dcd::$prefixesPsr0; + $loader->classMap = ComposerStaticInit2bc4f313dba415539e266f7ac2c87dcd::$classMap; }, null, ClassLoader::class); } diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index bcd37877..360dacfc 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -2,18 +2,24 @@ "packages": [ { "name": "apimatic/core", - "version": "0.3.17", - "version_normalized": "0.3.17.0", + "version": "0.3.16", + "version_normalized": "0.3.16.0", "source": { "type": "git", "url": "https://github.com/apimatic/core-lib-php.git", - "reference": "a48a583f686ee3786432b976c795a2817ec095b3" + "reference": "ae4ab4ca26a41be41718f33c703d67b7a767c07b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/apimatic/core-lib-php/zipball/a48a583f686ee3786432b976c795a2817ec095b3", - "reference": "a48a583f686ee3786432b976c795a2817ec095b3", - "shasum": "" + "url": "https://api.github.com/repos/apimatic/core-lib-php/zipball/ae4ab4ca26a41be41718f33c703d67b7a767c07b", + "reference": "ae4ab4ca26a41be41718f33c703d67b7a767c07b", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "apimatic/core-interfaces": "~0.1.5", @@ -32,7 +38,7 @@ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "squizlabs/php_codesniffer": "^3.5" }, - "time": "2026-01-27T05:14:10+00:00", + "time": "2025-11-25T04:42:27+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -54,7 +60,7 @@ ], "support": { "issues": "https://github.com/apimatic/core-lib-php/issues", - "source": "https://github.com/apimatic/core-lib-php/tree/0.3.17" + "source": "https://github.com/apimatic/core-lib-php/tree/0.3.16" }, "install-path": "../apimatic/core" }, @@ -288,6 +294,94 @@ }, "install-path": "../clue/stream-filter" }, + { + "name": "composer/pcre", + "version": "3.3.2", + "version_normalized": "3.3.2.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "time": "2024-11-12T16:29:46+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "install-path": "./pcre" + }, { "name": "ezyang/htmlpurifier", "version": "v4.19.0", @@ -541,18 +635,24 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.9.0", - "version_normalized": "2.9.0.0", + "version": "2.8.0", + "version_normalized": "2.8.0.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", - "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", - "shasum": "" + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^7.2.5 || ^8.0", @@ -567,13 +667,12 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "jshttp/mime-db": "1.54.0.1", "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, - "time": "2026-03-10T16:41:02+00:00", + "time": "2025-08-23T21:21:41+00:00", "type": "library", "extra": { "bamarni-bin": { @@ -641,38 +740,65 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.9.0" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], "install-path": "../guzzlehttp/psr7" }, { "name": "maennchen/zipstream-php", - "version": "2.1.0", - "version_normalized": "2.1.0.0", + "version": "3.1.2", + "version_normalized": "3.1.2.0", "source": { "type": "git", "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58" + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/c4c5803cc1f93df3d2448478ef79394a5981cc58", - "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58", - "shasum": "" + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { - "myclabs/php-enum": "^1.5", - "php": ">= 7.1", - "psr/http-message": "^1.0", - "symfony/polyfill-mbstring": "^1.0" + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.2" }, "require-dev": { + "brianium/paratest": "^7.7", "ext-zip": "*", - "guzzlehttp/guzzle": ">= 6.3", + "friendsofphp/php-cs-fixer": "^3.16", + "guzzlehttp/guzzle": "^7.5", "mikey179/vfsstream": "^1.6", - "phpunit/phpunit": ">= 7.5" + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^11.0", + "vimeo/psalm": "^6.0" }, - "time": "2020-05-30T13:11:16+00:00", + "suggest": { + "guzzlehttp/psr7": "^2.4", + "psr/http-message": "^2.0" + }, + "time": "2025-01-27T12:07:53+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -709,8 +835,14 @@ ], "support": { "issues": "https://github.com/maennchen/ZipStream-PHP/issues", - "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.1.0" + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.2" }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], "install-path": "../maennchen/zipstream-php" }, { @@ -826,62 +958,6 @@ }, "install-path": "../markbaker/matrix" }, - { - "name": "myclabs/php-enum", - "version": "1.8.5", - "version_normalized": "1.8.5.0", - "source": { - "type": "git", - "url": "https://github.com/myclabs/php-enum.git", - "reference": "e7be26966b7398204a234f8673fdad5ac6277802" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/php-enum/zipball/e7be26966b7398204a234f8673fdad5ac6277802", - "reference": "e7be26966b7398204a234f8673fdad5ac6277802", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.3 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "1.*", - "vimeo/psalm": "^4.6.2 || ^5.2" - }, - "time": "2025-01-14T11:49:03+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "MyCLabs\\Enum\\": "src/" - }, - "classmap": [ - "stubs/Stringable.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP Enum contributors", - "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" - } - ], - "description": "PHP Enum implementation", - "homepage": "https://github.com/myclabs/php-enum", - "keywords": [ - "enum" - ], - "support": { - "issues": "https://github.com/myclabs/php-enum/issues", - "source": "https://github.com/myclabs/php-enum/tree/1.8.5" - }, - "install-path": "../myclabs/php-enum" - }, { "name": "nyholm/psr7", "version": "1.8.2", @@ -955,17 +1031,17 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.8.2", - "version_normalized": "2.8.2.0", + "version": "v3.1.3", + "version_normalized": "3.1.3.0", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226" + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/e30811f7bc69e4b5b6d5783e712c06c8eabf0226", - "reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", "shasum": "", "mirrors": [ { @@ -975,13 +1051,15 @@ ] }, "require": { - "php": "^7|^8" + "php": "^8" }, "require-dev": { - "phpunit/phpunit": "^6|^7|^8|^9", - "vimeo/psalm": "^1|^2|^3|^4" + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" }, - "time": "2025-09-24T15:12:37+00:00", + "time": "2025-09-24T15:06:41+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1308,18 +1386,24 @@ }, { "name": "php-http/message", - "version": "1.16.2", - "version_normalized": "1.16.2.0", + "version": "1.16.1", + "version_normalized": "1.16.1.0", "source": { "type": "git", "url": "https://github.com/php-http/message.git", - "reference": "06dd5e8562f84e641bf929bfe699ee0f5ce8080a" + "reference": "5997f3289332c699fa2545c427826272498a2088" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/message/zipball/06dd5e8562f84e641bf929bfe699ee0f5ce8080a", - "reference": "06dd5e8562f84e641bf929bfe699ee0f5ce8080a", - "shasum": "" + "url": "https://api.github.com/repos/php-http/message/zipball/5997f3289332c699fa2545c427826272498a2088", + "reference": "5997f3289332c699fa2545c427826272498a2088", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "clue/stream-filter": "^1.5", @@ -1344,7 +1428,7 @@ "laminas/laminas-diactoros": "Used with Diactoros Factories", "slim/slim": "Used with Slim Framework PSR-7 implementation" }, - "time": "2024-10-02T11:34:13+00:00", + "time": "2024-03-07T13:22:09+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1374,24 +1458,30 @@ ], "support": { "issues": "https://github.com/php-http/message/issues", - "source": "https://github.com/php-http/message/tree/1.16.2" + "source": "https://github.com/php-http/message/tree/1.16.1" }, "install-path": "../php-http/message" }, { "name": "php-http/multipart-stream-builder", - "version": "1.4.2", - "version_normalized": "1.4.2.0", + "version": "1.3.1", + "version_normalized": "1.3.1.0", "source": { "type": "git", "url": "https://github.com/php-http/multipart-stream-builder.git", - "reference": "10086e6de6f53489cca5ecc45b6f468604d3460e" + "reference": "ed56da23b95949ae4747378bed8a5b61a2fdae24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/10086e6de6f53489cca5ecc45b6f468604d3460e", - "reference": "10086e6de6f53489cca5ecc45b6f468604d3460e", - "shasum": "" + "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/ed56da23b95949ae4747378bed8a5b61a2fdae24", + "reference": "ed56da23b95949ae4747378bed8a5b61a2fdae24", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^7.1 || ^8.0", @@ -1404,7 +1494,7 @@ "php-http/message-factory": "^1.0.2", "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" }, - "time": "2024-09-04T13:22:54+00:00", + "time": "2024-06-10T14:51:55+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1433,7 +1523,7 @@ ], "support": { "issues": "https://github.com/php-http/multipart-stream-builder/issues", - "source": "https://github.com/php-http/multipart-stream-builder/tree/1.4.2" + "source": "https://github.com/php-http/multipart-stream-builder/tree/1.3.1" }, "install-path": "../php-http/multipart-stream-builder" }, @@ -1498,18 +1588,24 @@ }, { "name": "phpmailer/phpmailer", - "version": "v6.12.0", - "version_normalized": "6.12.0.0", + "version": "v6.11.1", + "version_normalized": "6.11.1.0", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "d1ac35d784bf9f5e61b424901d5a014967f15b12" + "reference": "d9e3b36b47f04b497a0164c5a20f92acb4593284" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/d1ac35d784bf9f5e61b424901d5a014967f15b12", - "reference": "d1ac35d784bf9f5e61b424901d5a014967f15b12", - "shasum": "" + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/d9e3b36b47f04b497a0164c5a20f92acb4593284", + "reference": "d9e3b36b47f04b497a0164c5a20f92acb4593284", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-ctype": "*", @@ -1529,6 +1625,7 @@ }, "suggest": { "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", + "ext-imap": "Needed to support advanced email address parsing according to RFC822", "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", "ext-openssl": "Needed for secure SMTP sending and DKIM signing", "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", @@ -1538,7 +1635,7 @@ "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" }, - "time": "2025-10-15T16:49:08+00:00", + "time": "2025-09-30T11:54:53+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1570,24 +1667,36 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.12.0" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.11.1" }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], "install-path": "../phpmailer/phpmailer" }, { "name": "phpoffice/math", - "version": "0.3.0", - "version_normalized": "0.3.0.0", + "version": "0.2.0", + "version_normalized": "0.2.0.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/Math.git", - "reference": "fc31c8f57a7a81f962cbf389fd89f4d9d06fc99a" + "reference": "fc2eb6d1a61b058d5dac77197059db30ee3c8329" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/Math/zipball/fc31c8f57a7a81f962cbf389fd89f4d9d06fc99a", - "reference": "fc31c8f57a7a81f962cbf389fd89f4d9d06fc99a", - "shasum": "" + "url": "https://api.github.com/repos/PHPOffice/Math/zipball/fc2eb6d1a61b058d5dac77197059db30ee3c8329", + "reference": "fc2eb6d1a61b058d5dac77197059db30ee3c8329", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-dom": "*", @@ -1598,7 +1707,7 @@ "phpstan/phpstan": "^0.12.88 || ^1.0.0", "phpunit/phpunit": "^7.0 || ^9.0" }, - "time": "2025-05-29T08:31:49+00:00", + "time": "2024-08-12T07:30:45+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1625,7 +1734,7 @@ ], "support": { "issues": "https://github.com/PHPOffice/Math/issues", - "source": "https://github.com/PHPOffice/Math/tree/0.3.0" + "source": "https://github.com/PHPOffice/Math/tree/0.2.0" }, "install-path": "../phpoffice/math" }, @@ -1701,20 +1810,27 @@ }, { "name": "phpoffice/phpspreadsheet", - "version": "1.25.2", - "version_normalized": "1.25.2.0", + "version": "1.30.1", + "version_normalized": "1.30.1.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "a317a09e7def49852400a4b3eca4a4b0790ceeb5" + "reference": "fa8257a579ec623473eabfe49731de5967306c4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/a317a09e7def49852400a4b3eca4a4b0790ceeb5", - "reference": "a317a09e7def49852400a4b3eca4a4b0790ceeb5", - "shasum": "" + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/fa8257a579ec623473eabfe49731de5967306c4c", + "reference": "fa8257a579ec623473eabfe49731de5967306c4c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { + "composer/pcre": "^1||^2||^3", "ext-ctype": "*", "ext-dom": "*", "ext-fileinfo": "*", @@ -1729,26 +1845,26 @@ "ext-zip": "*", "ext-zlib": "*", "ezyang/htmlpurifier": "^4.15", - "maennchen/zipstream-php": "^2.1", + "maennchen/zipstream-php": "^2.1 || ^3.0", "markbaker/complex": "^3.0", "markbaker/matrix": "^3.0", - "php": "^7.3 || ^8.0", + "php": ">=7.4.0 <8.5.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0", "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "dev-master", - "dompdf/dompdf": "^1.0 || ^2.0", + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "dompdf/dompdf": "^1.0 || ^2.0 || ^3.0", "friendsofphp/php-cs-fixer": "^3.2", - "mitoteam/jpgraph": "10.2.4", - "mpdf/mpdf": "8.1.1", + "mitoteam/jpgraph": "^10.3", + "mpdf/mpdf": "^8.1.1", "phpcompatibility/php-compatibility": "^9.3", "phpstan/phpstan": "^1.1", "phpstan/phpstan-phpunit": "^1.0", "phpunit/phpunit": "^8.5 || ^9.0", "squizlabs/php_codesniffer": "^3.7", - "tecnickcom/tcpdf": "6.5" + "tecnickcom/tcpdf": "^6.5" }, "suggest": { "dompdf/dompdf": "Option for rendering PDF with PDF Writer", @@ -1757,7 +1873,7 @@ "mpdf/mpdf": "Option for rendering PDF with PDF Writer", "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" }, - "time": "2022-09-25T17:21:01+00:00", + "time": "2025-10-26T16:01:04+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1803,52 +1919,59 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.25.2" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.1" }, "install-path": "../phpoffice/phpspreadsheet" }, { "name": "phpoffice/phpword", - "version": "1.4.0", - "version_normalized": "1.4.0.0", + "version": "1.3.0", + "version_normalized": "1.3.0.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PHPWord.git", - "reference": "6d75328229bc93790b37e93741adf70646cea958" + "reference": "8392134ce4b5dba65130ba956231a1602b848b7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PHPWord/zipball/6d75328229bc93790b37e93741adf70646cea958", - "reference": "6d75328229bc93790b37e93741adf70646cea958", - "shasum": "" + "url": "https://api.github.com/repos/PHPOffice/PHPWord/zipball/8392134ce4b5dba65130ba956231a1602b848b7f", + "reference": "8392134ce4b5dba65130ba956231a1602b848b7f", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-dom": "*", - "ext-gd": "*", "ext-json": "*", "ext-xml": "*", - "ext-zip": "*", "php": "^7.1|^8.0", - "phpoffice/math": "^0.3" + "phpoffice/math": "^0.2" }, "require-dev": { - "dompdf/dompdf": "^2.0 || ^3.0", + "dompdf/dompdf": "^2.0", + "ext-gd": "*", "ext-libxml": "*", + "ext-zip": "*", "friendsofphp/php-cs-fixer": "^3.3", - "mpdf/mpdf": "^7.0 || ^8.0", + "mpdf/mpdf": "^8.1", "phpmd/phpmd": "^2.13", - "phpstan/phpstan": "^0.12.88 || ^1.0.0", - "phpstan/phpstan-phpunit": "^1.0 || ^2.0", + "phpstan/phpstan-phpunit": "@stable", "phpunit/phpunit": ">=7.0", "symfony/process": "^4.4 || ^5.0", "tecnickcom/tcpdf": "^6.5" }, "suggest": { "dompdf/dompdf": "Allows writing PDF", + "ext-gd2": "Allows adding images", "ext-xmlwriter": "Allows writing OOXML and ODF", - "ext-xsl": "Allows applying XSL style sheet to headers, to main document part, and to footers of an OOXML template" + "ext-xsl": "Allows applying XSL style sheet to headers, to main document part, and to footers of an OOXML template", + "ext-zip": "Allows writing OOXML and ODF" }, - "time": "2025-06-05T10:32:36+00:00", + "time": "2024-08-30T18:03:42+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1858,7 +1981,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0-only" + "LGPL-3.0" ], "authors": [ { @@ -1914,7 +2037,7 @@ ], "support": { "issues": "https://github.com/PHPOffice/PHPWord/issues", - "source": "https://github.com/PHPOffice/PHPWord/tree/1.4.0" + "source": "https://github.com/PHPOffice/PHPWord/tree/1.3.0" }, "install-path": "../phpoffice/phpword" }, @@ -2094,24 +2217,30 @@ }, { "name": "psr/http-factory", - "version": "1.1.0", - "version_normalized": "1.1.0.0", + "version": "1.0.2", + "version_normalized": "1.0.2.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + "reference": "e616d01114759c4c489f93b099585439f795fe35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "shasum": "" + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { - "php": ">=7.1", + "php": ">=7.0.0", "psr/http-message": "^1.0 || ^2.0" }, - "time": "2024-04-15T12:06:14+00:00", + "time": "2023-04-10T20:10:41+00:00", "type": "library", "extra": { "branch-alias": { @@ -2134,7 +2263,7 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "description": "Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -2146,33 +2275,39 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory" + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" }, "install-path": "../psr/http-factory" }, { "name": "psr/http-message", - "version": "1.1", - "version_normalized": "1.1.0.0", + "version": "2.0", + "version_normalized": "2.0.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", - "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", - "shasum": "" + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": "^7.2 || ^8.0" }, - "time": "2023-04-04T09:50:52+00:00", + "time": "2023-04-04T09:54:51+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "2.0.x-dev" } }, "installation-source": "dist", @@ -2188,7 +2323,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", @@ -2202,39 +2337,45 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/1.1" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, "install-path": "../psr/http-message" }, { "name": "psr/log", - "version": "1.1.4", - "version_normalized": "1.1.4.0", + "version": "3.0.1", + "version_normalized": "3.0.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "79dff0b268932c640297f5208d6298f71855c03e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" + "url": "https://api.github.com/repos/php-fig/log/zipball/79dff0b268932c640297f5208d6298f71855c03e", + "reference": "79dff0b268932c640297f5208d6298f71855c03e", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, - "time": "2021-05-03T11:20:27+00:00", + "time": "2024-08-21T13:31:24+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2255,33 +2396,39 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/3.0.1" }, "install-path": "../psr/log" }, { "name": "psr/simple-cache", - "version": "1.0.1", - "version_normalized": "1.0.1.0", + "version": "3.0.0", + "version_normalized": "3.0.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "shasum": "" + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, - "time": "2017-10-23T01:57:42+00:00", + "time": "2021-10-29T13:26:27+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0.x-dev" } }, "installation-source": "dist", @@ -2297,7 +2444,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interfaces for simple caching", @@ -2309,7 +2456,7 @@ "simple-cache" ], "support": { - "source": "https://github.com/php-fig/simple-cache/tree/master" + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, "install-path": "../psr/simple-cache" }, @@ -2417,23 +2564,29 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.4", - "version_normalized": "2.5.4.0", + "version": "v3.6.0", + "version_normalized": "3.6.0.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/605389f2a7e5625f273b53960dc46aeaf9c62918", - "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918", - "shasum": "" + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, - "time": "2024-09-25T14:11:13+00:00", + "time": "2024-09-25T14:21:43+00:00", "type": "library", "extra": { "thanks": { @@ -2441,7 +2594,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.6-dev" } }, "installation-source": "dist", @@ -2467,44 +2620,64 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.4" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "install-path": "../symfony/deprecation-contracts" }, { "name": "symfony/http-foundation", - "version": "v5.4.50", - "version_normalized": "5.4.50.0", + "version": "v8.0.1", + "version_normalized": "8.0.1.0", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "1a0706e8b8041046052ea2695eb8aeee04f97609" + "reference": "3690740e2e8b19d877f20d4f10b7a489cddf0fe2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1a0706e8b8041046052ea2695eb8aeee04f97609", - "reference": "1a0706e8b8041046052ea2695eb8aeee04f97609", - "shasum": "" + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3690740e2e8b19d877f20d4f10b7a489cddf0fe2", + "reference": "3690740e2e8b19d877f20d4f10b7a489cddf0fe2", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.1" + }, + "conflict": { + "doctrine/dbal": "<4.3" }, "require-dev": { - "predis/predis": "^1.0|^2.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", - "symfony/mime": "^4.4|^5.0|^6.0", - "symfony/rate-limiter": "^5.2|^6.0" + "doctrine/dbal": "^4.3", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0" }, - "suggest": { - "symfony/mime": "To use the file extension guesser" - }, - "time": "2025-11-03T12:58:48+00:00", + "time": "2025-12-07T11:23:24+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -2532,24 +2705,48 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.50" + "source": "https://github.com/symfony/http-foundation/tree/v8.0.1" }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "install-path": "../symfony/http-foundation" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.37.0", - "version_normalized": "1.37.0.0", + "version": "v1.32.0", + "version_normalized": "1.32.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", - "shasum": "" + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-iconv": "*", @@ -2561,7 +2758,7 @@ "suggest": { "ext-mbstring": "For best performance" }, - "time": "2026-04-10T17:25:58+00:00", + "time": "2024-12-23T08:48:59+00:00", "type": "library", "extra": { "thanks": { @@ -2602,79 +2799,24 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "install-path": "../symfony/polyfill-mbstring" }, - { - "name": "symfony/polyfill-php80", - "version": "v1.37.0", - "version_normalized": "1.37.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", - "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "time": "2026-04-10T16:19:22+00:00", - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "installation-source": "dist", - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" - }, - "install-path": "../symfony/polyfill-php80" - }, { "name": "tectalic/openai", "version": "v1.6.0", @@ -2861,18 +3003,24 @@ }, { "name": "topthink/think-helper", - "version": "v3.1.12", - "version_normalized": "3.1.12.0", + "version": "v3.1.11", + "version_normalized": "3.1.11.0", "source": { "type": "git", "url": "https://github.com/top-think/think-helper.git", - "reference": "fe277121112a8f1c872e169a733ca80bb11c4acb" + "reference": "1d6ada9b9f3130046bf6922fe1bd159c8d88a33c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/top-think/think-helper/zipball/fe277121112a8f1c872e169a733ca80bb11c4acb", - "reference": "fe277121112a8f1c872e169a733ca80bb11c4acb", - "shasum": "" + "url": "https://api.github.com/repos/top-think/think-helper/zipball/1d6ada9b9f3130046bf6922fe1bd159c8d88a33c", + "reference": "1d6ada9b9f3130046bf6922fe1bd159c8d88a33c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "php": ">=7.1.0" @@ -2880,7 +3028,7 @@ "require-dev": { "phpunit/phpunit": "^9.5" }, - "time": "2025-12-26T09:58:29+00:00", + "time": "2025-04-07T06:55:59+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -2904,24 +3052,30 @@ "description": "The ThinkPHP6 Helper Package", "support": { "issues": "https://github.com/top-think/think-helper/issues", - "source": "https://github.com/top-think/think-helper/tree/v3.1.12" + "source": "https://github.com/top-think/think-helper/tree/v3.1.11" }, "install-path": "../topthink/think-helper" }, { "name": "topthink/think-image", - "version": "v1.0.8", - "version_normalized": "1.0.8.0", + "version": "v1.0.7", + "version_normalized": "1.0.7.0", "source": { "type": "git", "url": "https://github.com/top-think/think-image.git", - "reference": "d1d748cbb2fe2f29fca6138cf96cb8b5113892f1" + "reference": "8586cf47f117481c6d415b20f7dedf62e79d5512" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/top-think/think-image/zipball/d1d748cbb2fe2f29fca6138cf96cb8b5113892f1", - "reference": "d1d748cbb2fe2f29fca6138cf96cb8b5113892f1", - "shasum": "" + "url": "https://api.github.com/repos/top-think/think-image/zipball/8586cf47f117481c6d415b20f7dedf62e79d5512", + "reference": "8586cf47f117481c6d415b20f7dedf62e79d5512", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "ext-gd": "*" @@ -2930,7 +3084,7 @@ "phpunit/phpunit": "4.8.*", "topthink/framework": "^5.0" }, - "time": "2024-08-07T10:06:35+00:00", + "time": "2016-09-29T06:05:43+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -2951,7 +3105,7 @@ "description": "The ThinkPHP5 Image Package", "support": { "issues": "https://github.com/top-think/think-image/issues", - "source": "https://github.com/top-think/think-image/tree/v1.0.8" + "source": "https://github.com/top-think/think-image/tree/master" }, "install-path": "../topthink/think-image" }, diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 91616f00..4cdddf9d 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'topthink/think', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '94b212fe7c6ec47113eeff7ab2125e0e1636d328', + 'reference' => 'bbd690ca0f68c671ece05e82edc88ee7a68b82ed', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -11,9 +11,9 @@ ), 'versions' => array( 'apimatic/core' => array( - 'pretty_version' => '0.3.17', - 'version' => '0.3.17.0', - 'reference' => 'a48a583f686ee3786432b976c795a2817ec095b3', + 'pretty_version' => '0.3.16', + 'version' => '0.3.16.0', + 'reference' => 'ae4ab4ca26a41be41718f33c703d67b7a767c07b', 'type' => 'library', 'install_path' => __DIR__ . '/../apimatic/core', 'aliases' => array(), @@ -55,6 +55,15 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'composer/pcre' => array( + 'pretty_version' => '3.3.2', + 'version' => '3.3.2.0', + 'reference' => 'b2bed4734f0cc156ee1fe9c0da2550420d99a21e', + 'type' => 'library', + 'install_path' => __DIR__ . '/./pcre', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'ezyang/htmlpurifier' => array( 'pretty_version' => 'v4.19.0', 'version' => '4.19.0.0', @@ -83,18 +92,18 @@ 'dev_requirement' => false, ), 'guzzlehttp/psr7' => array( - 'pretty_version' => '2.9.0', - 'version' => '2.9.0.0', - 'reference' => '7d0ed42f28e42d61352a7a79de682e5e67fec884', + 'pretty_version' => '2.8.0', + 'version' => '2.8.0.0', + 'reference' => '21dc724a0583619cd1652f673303492272778051', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'aliases' => array(), 'dev_requirement' => false, ), 'maennchen/zipstream-php' => array( - 'pretty_version' => '2.1.0', - 'version' => '2.1.0.0', - 'reference' => 'c4c5803cc1f93df3d2448478ef79394a5981cc58', + 'pretty_version' => '3.1.2', + 'version' => '3.1.2.0', + 'reference' => 'aeadcf5c412332eb426c0f9b4485f6accba2a99f', 'type' => 'library', 'install_path' => __DIR__ . '/../maennchen/zipstream-php', 'aliases' => array(), @@ -118,15 +127,6 @@ 'aliases' => array(), 'dev_requirement' => false, ), - 'myclabs/php-enum' => array( - 'pretty_version' => '1.8.5', - 'version' => '1.8.5.0', - 'reference' => 'e7be26966b7398204a234f8673fdad5ac6277802', - 'type' => 'library', - 'install_path' => __DIR__ . '/../myclabs/php-enum', - 'aliases' => array(), - 'dev_requirement' => false, - ), 'nyholm/psr7' => array( 'pretty_version' => '1.8.2', 'version' => '1.8.2.0', @@ -137,9 +137,9 @@ 'dev_requirement' => false, ), 'paragonie/constant_time_encoding' => array( - 'pretty_version' => 'v2.8.2', - 'version' => '2.8.2.0', - 'reference' => 'e30811f7bc69e4b5b6d5783e712c06c8eabf0226', + 'pretty_version' => 'v3.1.3', + 'version' => '3.1.3.0', + 'reference' => 'd5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77', 'type' => 'library', 'install_path' => __DIR__ . '/../paragonie/constant_time_encoding', 'aliases' => array(), @@ -194,9 +194,9 @@ 'dev_requirement' => false, ), 'php-http/message' => array( - 'pretty_version' => '1.16.2', - 'version' => '1.16.2.0', - 'reference' => '06dd5e8562f84e641bf929bfe699ee0f5ce8080a', + 'pretty_version' => '1.16.1', + 'version' => '1.16.1.0', + 'reference' => '5997f3289332c699fa2545c427826272498a2088', 'type' => 'library', 'install_path' => __DIR__ . '/../php-http/message', 'aliases' => array(), @@ -209,9 +209,9 @@ ), ), 'php-http/multipart-stream-builder' => array( - 'pretty_version' => '1.4.2', - 'version' => '1.4.2.0', - 'reference' => '10086e6de6f53489cca5ecc45b6f468604d3460e', + 'pretty_version' => '1.3.1', + 'version' => '1.3.1.0', + 'reference' => 'ed56da23b95949ae4747378bed8a5b61a2fdae24', 'type' => 'library', 'install_path' => __DIR__ . '/../php-http/multipart-stream-builder', 'aliases' => array(), @@ -227,18 +227,18 @@ 'dev_requirement' => false, ), 'phpmailer/phpmailer' => array( - 'pretty_version' => 'v6.12.0', - 'version' => '6.12.0.0', - 'reference' => 'd1ac35d784bf9f5e61b424901d5a014967f15b12', + 'pretty_version' => 'v6.11.1', + 'version' => '6.11.1.0', + 'reference' => 'd9e3b36b47f04b497a0164c5a20f92acb4593284', 'type' => 'library', 'install_path' => __DIR__ . '/../phpmailer/phpmailer', 'aliases' => array(), 'dev_requirement' => false, ), 'phpoffice/math' => array( - 'pretty_version' => '0.3.0', - 'version' => '0.3.0.0', - 'reference' => 'fc31c8f57a7a81f962cbf389fd89f4d9d06fc99a', + 'pretty_version' => '0.2.0', + 'version' => '0.2.0.0', + 'reference' => 'fc2eb6d1a61b058d5dac77197059db30ee3c8329', 'type' => 'library', 'install_path' => __DIR__ . '/../phpoffice/math', 'aliases' => array(), @@ -254,18 +254,18 @@ 'dev_requirement' => false, ), 'phpoffice/phpspreadsheet' => array( - 'pretty_version' => '1.25.2', - 'version' => '1.25.2.0', - 'reference' => 'a317a09e7def49852400a4b3eca4a4b0790ceeb5', + 'pretty_version' => '1.30.1', + 'version' => '1.30.1.0', + 'reference' => 'fa8257a579ec623473eabfe49731de5967306c4c', 'type' => 'library', 'install_path' => __DIR__ . '/../phpoffice/phpspreadsheet', 'aliases' => array(), 'dev_requirement' => false, ), 'phpoffice/phpword' => array( - 'pretty_version' => '1.4.0', - 'version' => '1.4.0.0', - 'reference' => '6d75328229bc93790b37e93741adf70646cea958', + 'pretty_version' => '1.3.0', + 'version' => '1.3.0.0', + 'reference' => '8392134ce4b5dba65130ba956231a1602b848b7f', 'type' => 'library', 'install_path' => __DIR__ . '/../phpoffice/phpword', 'aliases' => array(), @@ -297,9 +297,9 @@ ), ), 'psr/http-factory' => array( - 'pretty_version' => '1.1.0', - 'version' => '1.1.0.0', - 'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a', + 'pretty_version' => '1.0.2', + 'version' => '1.0.2.0', + 'reference' => 'e616d01114759c4c489f93b099585439f795fe35', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-factory', 'aliases' => array(), @@ -313,9 +313,9 @@ ), ), 'psr/http-message' => array( - 'pretty_version' => '1.1', - 'version' => '1.1.0.0', - 'reference' => 'cb6ce4845ce34a8ad9e68117c10ee90a29919eba', + 'pretty_version' => '2.0', + 'version' => '2.0.0.0', + 'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-message', 'aliases' => array(), @@ -329,18 +329,18 @@ ), ), 'psr/log' => array( - 'pretty_version' => '1.1.4', - 'version' => '1.1.4.0', - 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11', + 'pretty_version' => '3.0.1', + 'version' => '3.0.1.0', + 'reference' => '79dff0b268932c640297f5208d6298f71855c03e', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/log', 'aliases' => array(), 'dev_requirement' => false, ), 'psr/simple-cache' => array( - 'pretty_version' => '1.0.1', - 'version' => '1.0.1.0', - 'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b', + 'pretty_version' => '3.0.0', + 'version' => '3.0.0.0', + 'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/simple-cache', 'aliases' => array(), @@ -365,41 +365,32 @@ 'dev_requirement' => false, ), 'symfony/deprecation-contracts' => array( - 'pretty_version' => 'v2.5.4', - 'version' => '2.5.4.0', - 'reference' => '605389f2a7e5625f273b53960dc46aeaf9c62918', + 'pretty_version' => 'v3.6.0', + 'version' => '3.6.0.0', + 'reference' => '63afe740e99a13ba87ec199bb07bbdee937a5b62', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/http-foundation' => array( - 'pretty_version' => 'v5.4.50', - 'version' => '5.4.50.0', - 'reference' => '1a0706e8b8041046052ea2695eb8aeee04f97609', + 'pretty_version' => 'v8.0.1', + 'version' => '8.0.1.0', + 'reference' => '3690740e2e8b19d877f20d4f10b7a489cddf0fe2', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/http-foundation', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/polyfill-mbstring' => array( - 'pretty_version' => 'v1.37.0', - 'version' => '1.37.0.0', - 'reference' => '6a21eb99c6973357967f6ce3708cd55a6bec6315', + 'pretty_version' => 'v1.32.0', + 'version' => '1.32.0.0', + 'reference' => '6d857f4d76bd4b343eac26d6b539585d2bc56493', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', 'aliases' => array(), 'dev_requirement' => false, ), - 'symfony/polyfill-php80' => array( - 'pretty_version' => 'v1.37.0', - 'version' => '1.37.0.0', - 'reference' => 'dfb55726c3a76ea3b6459fcfda1ec2d80a682411', - 'type' => 'library', - 'install_path' => __DIR__ . '/../symfony/polyfill-php80', - 'aliases' => array(), - 'dev_requirement' => false, - ), 'tectalic/openai' => array( 'pretty_version' => 'v1.6.0', 'version' => '1.6.0.0', @@ -421,7 +412,7 @@ 'topthink/think' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '94b212fe7c6ec47113eeff7ab2125e0e1636d328', + 'reference' => 'bbd690ca0f68c671ece05e82edc88ee7a68b82ed', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -437,18 +428,18 @@ 'dev_requirement' => false, ), 'topthink/think-helper' => array( - 'pretty_version' => 'v3.1.12', - 'version' => '3.1.12.0', - 'reference' => 'fe277121112a8f1c872e169a733ca80bb11c4acb', + 'pretty_version' => 'v3.1.11', + 'version' => '3.1.11.0', + 'reference' => '1d6ada9b9f3130046bf6922fe1bd159c8d88a33c', 'type' => 'library', 'install_path' => __DIR__ . '/../topthink/think-helper', 'aliases' => array(), 'dev_requirement' => false, ), 'topthink/think-image' => array( - 'pretty_version' => 'v1.0.8', - 'version' => '1.0.8.0', - 'reference' => 'd1d748cbb2fe2f29fca6138cf96cb8b5113892f1', + 'pretty_version' => 'v1.0.7', + 'version' => '1.0.7.0', + 'reference' => '8586cf47f117481c6d415b20f7dedf62e79d5512', 'type' => 'library', 'install_path' => __DIR__ . '/../topthink/think-image', 'aliases' => array(), diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php deleted file mode 100644 index d826bd13..00000000 --- a/vendor/composer/platform_check.php +++ /dev/null @@ -1,25 +0,0 @@ -= 70300)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.0". You are running ' . PHP_VERSION . '.'; -} - -if ($issues) { - if (!headers_sent()) { - header('HTTP/1.1 500 Internal Server Error'); - } - if (!ini_get('display_errors')) { - if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { - fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); - } elseif (!headers_sent()) { - echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; - } - } - throw new \RuntimeException( - 'Composer detected issues in your platform: ' . implode(' ', $issues) - ); -} diff --git a/vendor/phpmailer/phpmailer/README.md b/vendor/phpmailer/phpmailer/README.md index 646649c9..51c97517 100644 --- a/vendor/phpmailer/phpmailer/README.md +++ b/vendor/phpmailer/phpmailer/README.md @@ -48,7 +48,7 @@ This software is distributed under the [LGPL 2.1](https://www.gnu.org/licenses/o PHPMailer is available on [Packagist](https://packagist.org/packages/phpmailer/phpmailer) (using semantic versioning), and installation via [Composer](https://getcomposer.org) is the recommended way to install PHPMailer. Just add this line to your `composer.json` file: ```json -"phpmailer/phpmailer": "^6.12.0" +"phpmailer/phpmailer": "^6.11.1" ``` or run diff --git a/vendor/phpmailer/phpmailer/VERSION b/vendor/phpmailer/phpmailer/VERSION index d4e6cb42..fac714a3 100644 --- a/vendor/phpmailer/phpmailer/VERSION +++ b/vendor/phpmailer/phpmailer/VERSION @@ -1 +1 @@ -6.12.0 +6.11.1 diff --git a/vendor/phpmailer/phpmailer/composer.json b/vendor/phpmailer/phpmailer/composer.json index 7b008b7c..e762d59d 100644 --- a/vendor/phpmailer/phpmailer/composer.json +++ b/vendor/phpmailer/phpmailer/composer.json @@ -49,14 +49,15 @@ }, "suggest": { "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", + "ext-imap": "Needed to support advanced email address parsing according to RFC822", "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", "ext-openssl": "Needed for secure SMTP sending and DKIM signing", "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", "league/oauth2-google": "Needed for Google XOAUTH2 authentication", "psr/log": "For optional PSR-3 debug logging", - "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" }, "autoload": { "psr-4": { @@ -71,6 +72,7 @@ "license": "LGPL-2.1-only", "scripts": { "check": "./vendor/bin/phpcs", + "style": "./vendor/bin/phpcbf", "test": "./vendor/bin/phpunit --no-coverage", "coverage": "./vendor/bin/phpunit", "lint": [ diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-es.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-es.php index 4e74bfb7..a871824f 100644 --- a/vendor/phpmailer/phpmailer/language/phpmailer.lang-es.php +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-es.php @@ -9,7 +9,7 @@ */ $PHPMAILER_LANG['authenticate'] = 'Error SMTP: Imposible autentificar.'; -$PHPMAILER_LANG['buggy_php'] = 'Tu versión de PHP está afectada por un bug que puede resultar en mensajes corruptos. Para arreglarlo, cambia a enviar usando SMTP, deshabilita la opción mail.add_x_header en tu php.ini, cambia a MacOS o Linux, o actualiza tu PHP a la versión 7.0.17+ o 7.1.3+.'; +$PHPMAILER_LANG['buggy_php'] = 'Tu versión de PHP ha sido afectada por un bug que puede resultar en mensajes corruptos. Para arreglarlo, cambia a enviar usando SMTP, deshabilita la opción mail.add_x_header en tu php.ini, cambia a MacOS o Linux, o actualiza tu PHP a la versión 7.0.17+ o 7.1.3+.'; $PHPMAILER_LANG['connect_host'] = 'Error SMTP: Imposible conectar al servidor SMTP.'; $PHPMAILER_LANG['data_not_accepted'] = 'Error SMTP: Datos no aceptados.'; $PHPMAILER_LANG['empty_message'] = 'El cuerpo del mensaje está vacío.'; @@ -18,7 +18,7 @@ $PHPMAILER_LANG['execute'] = 'Imposible ejecutar: '; $PHPMAILER_LANG['extension_missing'] = 'Extensión faltante: '; $PHPMAILER_LANG['file_access'] = 'Imposible acceder al archivo: '; $PHPMAILER_LANG['file_open'] = 'Error de Archivo: Imposible abrir el archivo: '; -$PHPMAILER_LANG['from_failed'] = 'La(s) siguiente(s) direcciones de remitente fallaron: '; +$PHPMAILER_LANG['from_failed'] = 'La siguiente dirección de remitente falló: '; $PHPMAILER_LANG['instantiate'] = 'Imposible crear una instancia de la función Mail.'; $PHPMAILER_LANG['invalid_address'] = 'Imposible enviar: dirección de email inválido: '; $PHPMAILER_LANG['invalid_header'] = 'Nombre o valor de encabezado no válido'; @@ -34,3 +34,5 @@ $PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() falló.'; $PHPMAILER_LANG['smtp_detail'] = 'Detalle: '; $PHPMAILER_LANG['smtp_error'] = 'Error del servidor SMTP: '; $PHPMAILER_LANG['variable_set'] = 'No se pudo configurar la variable: '; +$PHPMAILER_LANG['imap_recommended'] = 'No se recomienda usar el analizador de direcciones simplificado. Instala la extensión IMAP de PHP para un análisis RFC822 más completo.'; +$PHPMAILER_LANG['deprecated_argument'] = 'El argumento $useimap ha quedado obsoleto'; diff --git a/vendor/phpmailer/phpmailer/src/PHPMailer.php b/vendor/phpmailer/phpmailer/src/PHPMailer.php index 4b3f34c8..0a8711f4 100644 --- a/vendor/phpmailer/phpmailer/src/PHPMailer.php +++ b/vendor/phpmailer/phpmailer/src/PHPMailer.php @@ -561,9 +561,9 @@ class PHPMailer * string $body the email body * string $from email address of sender * string $extra extra information of possible use - * "smtp_transaction_id' => last smtp transaction id + * 'smtp_transaction_id' => last smtp transaction id * - * @var string + * @var callable|callable-string */ public $action_function = ''; @@ -711,7 +711,7 @@ class PHPMailer * * @var array */ - protected $language = []; + protected static $language = []; /** * The number of errors encountered. @@ -768,7 +768,7 @@ class PHPMailer * * @var string */ - const VERSION = '6.12.0'; + const VERSION = '6.11.1'; /** * Error severity: message only, continue processing. @@ -1102,7 +1102,7 @@ class PHPMailer //At-sign is missing. $error_message = sprintf( '%s (%s): %s', - $this->lang('invalid_address'), + self::lang('invalid_address'), $kind, $address ); @@ -1187,7 +1187,7 @@ class PHPMailer if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) { $error_message = sprintf( '%s: %s', - $this->lang('Invalid recipient kind'), + self::lang('Invalid recipient kind'), $kind ); $this->setError($error_message); @@ -1201,7 +1201,7 @@ class PHPMailer if (!static::validateAddress($address)) { $error_message = sprintf( '%s (%s): %s', - $this->lang('invalid_address'), + self::lang('invalid_address'), $kind, $address ); @@ -1220,12 +1220,16 @@ class PHPMailer return true; } - } elseif (!array_key_exists(strtolower($address), $this->ReplyTo)) { - $this->ReplyTo[strtolower($address)] = [$address, $name]; + } else { + foreach ($this->ReplyTo as $replyTo) { + if (0 === strcasecmp($replyTo[0], $address)) { + return false; + } + } + $this->ReplyTo[] = [$address, $name]; return true; } - return false; } @@ -1238,15 +1242,18 @@ class PHPMailer * @see https://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation * * @param string $addrstr The address list string - * @param bool $useimap Whether to use the IMAP extension to parse the list + * @param null $useimap Deprecated argument since 6.11.0. * @param string $charset The charset to use when decoding the address list string. * * @return array */ - public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591) + public static function parseAddresses($addrstr, $useimap = null, $charset = self::CHARSET_ISO88591) { + if ($useimap !== null) { + trigger_error(self::lang('deprecated_argument'), E_USER_DEPRECATED); + } $addresses = []; - if ($useimap && function_exists('imap_rfc822_parse_adrlist')) { + if (function_exists('imap_rfc822_parse_adrlist')) { //Use this built-in parser if it's available $list = imap_rfc822_parse_adrlist($addrstr, ''); // Clear any potential IMAP errors to get rid of notices being thrown at end of script. @@ -1256,20 +1263,13 @@ class PHPMailer '.SYNTAX-ERROR.' !== $address->host && static::validateAddress($address->mailbox . '@' . $address->host) ) { - //Decode the name part if it's present and encoded + //Decode the name part if it's present and maybe encoded if ( - property_exists($address, 'personal') && - //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled - defined('MB_CASE_UPPER') && - preg_match('/^=\?.*\?=$/s', $address->personal) + property_exists($address, 'personal') + && is_string($address->personal) + && $address->personal !== '' ) { - $origCharset = mb_internal_encoding(); - mb_internal_encoding($charset); - //Undo any RFC2047-encoded spaces-as-underscores - $address->personal = str_replace('_', '=20', $address->personal); - //Decode the name - $address->personal = mb_decode_mimeheader($address->personal); - mb_internal_encoding($origCharset); + $address->personal = static::decodeHeader($address->personal, $charset); } $addresses[] = [ @@ -1280,40 +1280,51 @@ class PHPMailer } } else { //Use this simpler parser - $list = explode(',', $addrstr); - foreach ($list as $address) { - $address = trim($address); - //Is there a separate name part? - if (strpos($address, '<') === false) { - //No separate name, just use the whole thing - if (static::validateAddress($address)) { - $addresses[] = [ - 'name' => '', - 'address' => $address, - ]; - } - } else { - list($name, $email) = explode('<', $address); - $email = trim(str_replace('>', '', $email)); - $name = trim($name); - if (static::validateAddress($email)) { - //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled - //If this name is encoded, decode it - if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) { - $origCharset = mb_internal_encoding(); - mb_internal_encoding($charset); - //Undo any RFC2047-encoded spaces-as-underscores - $name = str_replace('_', '=20', $name); - //Decode the name - $name = mb_decode_mimeheader($name); - mb_internal_encoding($origCharset); - } - $addresses[] = [ - //Remove any surrounding quotes and spaces from the name - 'name' => trim($name, '\'" '), - 'address' => $email, - ]; - } + $addresses = static::parseSimplerAddresses($addrstr, $charset); + } + + return $addresses; + } + + /** + * Parse a string containing one or more RFC822-style comma-separated email addresses + * with the form "display name
" into an array of name/address pairs. + * Uses a simpler parser that does not require the IMAP extension but doesnt support + * the full RFC822 spec. For full RFC822 support, use the PHP IMAP extension. + * + * @param string $addrstr The address list string + * @param string $charset The charset to use when decoding the address list string. + * + * @return array + */ + protected static function parseSimplerAddresses($addrstr, $charset) + { + // Emit a runtime notice to recommend using the IMAP extension for full RFC822 parsing + trigger_error(self::lang('imap_recommended'), E_USER_NOTICE); + + $addresses = []; + $list = explode(',', $addrstr); + foreach ($list as $address) { + $address = trim($address); + //Is there a separate name part? + if (strpos($address, '<') === false) { + //No separate name, just use the whole thing + if (static::validateAddress($address)) { + $addresses[] = [ + 'name' => '', + 'address' => $address, + ]; + } + } else { + $parsed = static::parseEmailString($address); + $email = $parsed['email']; + if (static::validateAddress($email)) { + $name = static::decodeHeader($parsed['name'], $charset); + $addresses[] = [ + //Remove any surrounding quotes and spaces from the name + 'name' => trim($name, '\'" '), + 'address' => $email, + ]; } } } @@ -1321,6 +1332,42 @@ class PHPMailer return $addresses; } + /** + * Parse a string containing an email address with an optional name + * and divide it into a name and email address. + * + * @param string $input The email with name. + * + * @return array{name: string, email: string} + */ + private static function parseEmailString($input) + { + $input = trim((string)$input); + + if ($input === '') { + return ['name' => '', 'email' => '']; + } + + $pattern = '/^\s*(?:(?:"([^"]*)"|\'([^\']*)\'|([^<]*?))\s*)?<\s*([^>]+)\s*>\s*$/'; + if (preg_match($pattern, $input, $matches)) { + $name = ''; + // Double quotes including special scenarios. + if (isset($matches[1]) && $matches[1] !== '') { + $name = $matches[1]; + // Single quotes including special scenarios. + } elseif (isset($matches[2]) && $matches[2] !== '') { + $name = $matches[2]; + // Simplest scenario, name and email are in the format "Name ". + } elseif (isset($matches[3])) { + $name = trim($matches[3]); + } + + return ['name' => $name, 'email' => trim($matches[4])]; + } + + return ['name' => '', 'email' => $input]; + } + /** * Set the From and FromName properties. * @@ -1334,6 +1381,10 @@ class PHPMailer */ public function setFrom($address, $name = '', $auto = true) { + if (is_null($name)) { + //Helps avoid a deprecation warning in the preg_replace() below + $name = ''; + } $address = trim((string)$address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim //Don't validate now addresses with IDN. Will be done in send(). @@ -1345,7 +1396,7 @@ class PHPMailer ) { $error_message = sprintf( '%s (From): %s', - $this->lang('invalid_address'), + self::lang('invalid_address'), $address ); $this->setError($error_message); @@ -1601,7 +1652,7 @@ class PHPMailer && ini_get('mail.add_x_header') === '1' && stripos(PHP_OS, 'WIN') === 0 ) { - trigger_error($this->lang('buggy_php'), E_USER_WARNING); + trigger_error(self::lang('buggy_php'), E_USER_WARNING); } try { @@ -1631,7 +1682,7 @@ class PHPMailer call_user_func_array([$this, 'addAnAddress'], $params); } if (count($this->to) + count($this->cc) + count($this->bcc) < 1) { - throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL); + throw new Exception(self::lang('provide_address'), self::STOP_CRITICAL); } //Validate From, Sender, and ConfirmReadingTo addresses @@ -1648,7 +1699,7 @@ class PHPMailer if (!static::validateAddress($this->{$address_kind})) { $error_message = sprintf( '%s (%s): %s', - $this->lang('invalid_address'), + self::lang('invalid_address'), $address_kind, $this->{$address_kind} ); @@ -1670,7 +1721,7 @@ class PHPMailer $this->setMessageType(); //Refuse to send an empty message unless we are specifically allowing it if (!$this->AllowEmpty && empty($this->Body)) { - throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); + throw new Exception(self::lang('empty_message'), self::STOP_CRITICAL); } //Trim subject consistently @@ -1809,8 +1860,10 @@ class PHPMailer } else { $sendmailFmt = '%s -oi -f%s -t'; } + } elseif ($this->Mailer === 'qmail') { + $sendmailFmt = '%s'; } else { - //allow sendmail to choose a default envelope sender. It may + //Allow sendmail to choose a default envelope sender. It may //seem preferable to force it to use the From header as with //SMTP, but that introduces new problems (see //), and @@ -1828,33 +1881,35 @@ class PHPMailer foreach ($this->SingleToArray as $toAddr) { $mail = @popen($sendmail, 'w'); if (!$mail) { - throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } $this->edebug("To: {$toAddr}"); fwrite($mail, 'To: ' . $toAddr . "\n"); fwrite($mail, $header); fwrite($mail, $body); $result = pclose($mail); - $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); - $this->doCallback( - ($result === 0), - [[$addrinfo['address'], $addrinfo['name']]], - $this->cc, - $this->bcc, - $this->Subject, - $body, - $this->From, - [] - ); + $addrinfo = static::parseAddresses($toAddr, null, $this->CharSet); + foreach ($addrinfo as $addr) { + $this->doCallback( + ($result === 0), + [[$addr['address'], $addr['name']]], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + } $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); if (0 !== $result) { - throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } } } else { $mail = @popen($sendmail, 'w'); if (!$mail) { - throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } fwrite($mail, $header); fwrite($mail, $body); @@ -1871,7 +1926,7 @@ class PHPMailer ); $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); if (0 !== $result) { - throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + throw new Exception(self::lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } } @@ -2010,17 +2065,19 @@ class PHPMailer if ($this->SingleTo && count($toArr) > 1) { foreach ($toArr as $toAddr) { $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); - $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); - $this->doCallback( - $result, - [[$addrinfo['address'], $addrinfo['name']]], - $this->cc, - $this->bcc, - $this->Subject, - $body, - $this->From, - [] - ); + $addrinfo = static::parseAddresses($toAddr, null, $this->CharSet); + foreach ($addrinfo as $addr) { + $this->doCallback( + $result, + [[$addr['address'], $addr['name']]], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + } } } else { $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); @@ -2030,7 +2087,7 @@ class PHPMailer ini_set('sendmail_from', $old_from); } if (!$result) { - throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL); + throw new Exception(self::lang('instantiate'), self::STOP_CRITICAL); } return true; @@ -2116,12 +2173,12 @@ class PHPMailer $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; $bad_rcpt = []; if (!$this->smtpConnect($this->SMTPOptions)) { - throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); + throw new Exception(self::lang('smtp_connect_failed'), self::STOP_CRITICAL); } //If we have recipient addresses that need Unicode support, //but the server doesn't support it, stop here if ($this->UseSMTPUTF8 && !$this->smtp->getServerExt('SMTPUTF8')) { - throw new Exception($this->lang('no_smtputf8'), self::STOP_CRITICAL); + throw new Exception(self::lang('no_smtputf8'), self::STOP_CRITICAL); } //Sender already validated in preSend() if ('' === $this->Sender) { @@ -2133,7 +2190,7 @@ class PHPMailer $this->smtp->xclient($this->SMTPXClient); } if (!$this->smtp->mail($smtp_from)) { - $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); + $this->setError(self::lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); throw new Exception($this->ErrorInfo, self::STOP_CRITICAL); } @@ -2155,7 +2212,7 @@ class PHPMailer //Only send the DATA command if we have viable recipients if ((count($this->all_recipients) > count($bad_rcpt)) && !$this->smtp->data($header . $body)) { - throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL); + throw new Exception(self::lang('data_not_accepted'), self::STOP_CRITICAL); } $smtp_transaction_id = $this->smtp->getLastTransactionID(); @@ -2186,7 +2243,7 @@ class PHPMailer foreach ($bad_rcpt as $bad) { $errstr .= $bad['to'] . ': ' . $bad['error']; } - throw new Exception($this->lang('recipients_failed') . $errstr, self::STOP_CONTINUE); + throw new Exception(self::lang('recipients_failed') . $errstr, self::STOP_CONTINUE); } return true; @@ -2240,7 +2297,7 @@ class PHPMailer $hostinfo ) ) { - $this->edebug($this->lang('invalid_hostentry') . ' ' . trim($hostentry)); + $this->edebug(self::lang('invalid_hostentry') . ' ' . trim($hostentry)); //Not a valid host entry continue; } @@ -2252,7 +2309,7 @@ class PHPMailer //Check the host name is a valid name or IP address before trying to use it if (!static::isValidHost($hostinfo[2])) { - $this->edebug($this->lang('invalid_host') . ' ' . $hostinfo[2]); + $this->edebug(self::lang('invalid_host') . ' ' . $hostinfo[2]); continue; } $prefix = ''; @@ -2272,7 +2329,7 @@ class PHPMailer if (static::ENCRYPTION_STARTTLS === $secure || static::ENCRYPTION_SMTPS === $secure) { //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled if (!$sslext) { - throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL); + throw new Exception(self::lang('extension_missing') . 'openssl', self::STOP_CRITICAL); } } $host = $hostinfo[2]; @@ -2324,7 +2381,7 @@ class PHPMailer $this->oauth ) ) { - throw new Exception($this->lang('authenticate')); + throw new Exception(self::lang('authenticate')); } return true; @@ -2374,7 +2431,7 @@ class PHPMailer * * @return bool Returns true if the requested language was loaded, false otherwise. */ - public function setLanguage($langcode = 'en', $lang_path = '') + public static function setLanguage($langcode = 'en', $lang_path = '') { //Backwards compatibility for renamed language codes $renamed_langcodes = [ @@ -2423,6 +2480,9 @@ class PHPMailer 'smtp_error' => 'SMTP server error: ', 'variable_set' => 'Cannot set or reset variable: ', 'no_smtputf8' => 'Server does not support SMTPUTF8 needed to send to Unicode addresses', + 'imap_recommended' => 'Using simplified address parser is not recommended. ' . + 'Install the PHP IMAP extension for full RFC822 parsing.', + 'deprecated_argument' => 'Argument $useimap is deprecated', ]; if (empty($lang_path)) { //Calculate an absolute path so it can work if CWD is not here @@ -2489,7 +2549,7 @@ class PHPMailer } } } - $this->language = $PHPMAILER_LANG; + self::$language = $PHPMAILER_LANG; return $foundlang; //Returns false if language not found } @@ -2501,11 +2561,11 @@ class PHPMailer */ public function getTranslations() { - if (empty($this->language)) { - $this->setLanguage(); // Set the default language. + if (empty(self::$language)) { + self::setLanguage(); // Set the default language. } - return $this->language; + return self::$language; } /** @@ -2928,10 +2988,6 @@ class PHPMailer //Create unique IDs and preset boundaries $this->setBoundaries(); - if ($this->sign_key_file) { - $body .= $this->getMailMIME() . static::$LE; - } - $this->setWordWrap(); $bodyEncoding = $this->Encoding; @@ -2963,6 +3019,12 @@ class PHPMailer if (static::ENCODING_BASE64 !== $altBodyEncoding && static::hasLineLongerThanMax($this->AltBody)) { $altBodyEncoding = static::ENCODING_QUOTED_PRINTABLE; } + + if ($this->sign_key_file) { + $this->Encoding = $bodyEncoding; + $body .= $this->getMailMIME() . static::$LE; + } + //Use this as a preamble in all multipart message types $mimepre = ''; switch ($this->message_type) { @@ -3144,12 +3206,12 @@ class PHPMailer if ($this->isError()) { $body = ''; if ($this->exceptions) { - throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); + throw new Exception(self::lang('empty_message'), self::STOP_CRITICAL); } } elseif ($this->sign_key_file) { try { if (!defined('PKCS7_TEXT')) { - throw new Exception($this->lang('extension_missing') . 'openssl'); + throw new Exception(self::lang('extension_missing') . 'openssl'); } $file = tempnam(sys_get_temp_dir(), 'srcsign'); @@ -3187,7 +3249,7 @@ class PHPMailer $body = $parts[1]; } else { @unlink($signed); - throw new Exception($this->lang('signing') . openssl_error_string()); + throw new Exception(self::lang('signing') . openssl_error_string()); } } catch (Exception $exc) { $body = ''; @@ -3332,7 +3394,7 @@ class PHPMailer ) { try { if (!static::fileIsAccessible($path)) { - throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE); + throw new Exception(self::lang('file_access') . $path, self::STOP_CONTINUE); } //If a MIME type is not specified, try to work it out from the file name @@ -3345,7 +3407,7 @@ class PHPMailer $name = $filename; } if (!$this->validateEncoding($encoding)) { - throw new Exception($this->lang('encoding') . $encoding); + throw new Exception(self::lang('encoding') . $encoding); } $this->attachment[] = [ @@ -3506,11 +3568,11 @@ class PHPMailer { try { if (!static::fileIsAccessible($path)) { - throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE); + throw new Exception(self::lang('file_open') . $path, self::STOP_CONTINUE); } $file_buffer = file_get_contents($path); if (false === $file_buffer) { - throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE); + throw new Exception(self::lang('file_open') . $path, self::STOP_CONTINUE); } $file_buffer = $this->encodeString($file_buffer, $encoding); @@ -3563,9 +3625,9 @@ class PHPMailer $encoded = $this->encodeQP($str); break; default: - $this->setError($this->lang('encoding') . $encoding); + $this->setError(self::lang('encoding') . $encoding); if ($this->exceptions) { - throw new Exception($this->lang('encoding') . $encoding); + throw new Exception(self::lang('encoding') . $encoding); } break; } @@ -3671,6 +3733,42 @@ class PHPMailer return trim(static::normalizeBreaks($encoded)); } + /** + * Decode an RFC2047-encoded header value + * Attempts multiple strategies so it works even when the mbstring extension is disabled. + * + * @param string $value The header value to decode + * @param string $charset The target charset to convert to, defaults to ISO-8859-1 for BC + * + * @return string The decoded header value + */ + public static function decodeHeader($value, $charset = self::CHARSET_ISO88591) + { + if (!is_string($value) || $value === '') { + return ''; + } + // Detect the presence of any RFC2047 encoded-words + $hasEncodedWord = (bool) preg_match('/=\?.*\?=/s', $value); + if ($hasEncodedWord && defined('MB_CASE_UPPER')) { + $origCharset = mb_internal_encoding(); + // Always decode to UTF-8 to provide a consistent, modern output encoding. + mb_internal_encoding($charset); + if (PHP_VERSION_ID < 80300) { + // Undo any RFC2047-encoded spaces-as-underscores. + $value = str_replace('_', '=20', $value); + } else { + // PHP 8.3+ already interprets underscores as spaces. Remove additional + // linear whitespace between adjacent encoded words to avoid double spacing. + $value = preg_replace('/(\?=)\s+(=\?)/', '$1$2', $value); + } + // Decode the header value + $value = mb_decode_mimeheader($value); + mb_internal_encoding($origCharset); + } + + return $value; + } + /** * Check if a string contains multi-byte characters. * @@ -3840,7 +3938,7 @@ class PHPMailer } if (!$this->validateEncoding($encoding)) { - throw new Exception($this->lang('encoding') . $encoding); + throw new Exception(self::lang('encoding') . $encoding); } //Append to $attachment array @@ -3899,7 +3997,7 @@ class PHPMailer ) { try { if (!static::fileIsAccessible($path)) { - throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE); + throw new Exception(self::lang('file_access') . $path, self::STOP_CONTINUE); } //If a MIME type is not specified, try to work it out from the file name @@ -3908,7 +4006,7 @@ class PHPMailer } if (!$this->validateEncoding($encoding)) { - throw new Exception($this->lang('encoding') . $encoding); + throw new Exception(self::lang('encoding') . $encoding); } $filename = (string) static::mb_pathinfo($path, PATHINFO_BASENAME); @@ -3974,7 +4072,7 @@ class PHPMailer } if (!$this->validateEncoding($encoding)) { - throw new Exception($this->lang('encoding') . $encoding); + throw new Exception(self::lang('encoding') . $encoding); } //Append to $attachment array @@ -4231,7 +4329,7 @@ class PHPMailer } if (strpbrk($name . $value, "\r\n") !== false) { if ($this->exceptions) { - throw new Exception($this->lang('invalid_header')); + throw new Exception(self::lang('invalid_header')); } return false; @@ -4255,15 +4353,15 @@ class PHPMailer if ('smtp' === $this->Mailer && null !== $this->smtp) { $lasterror = $this->smtp->getError(); if (!empty($lasterror['error'])) { - $msg .= ' ' . $this->lang('smtp_error') . $lasterror['error']; + $msg .= ' ' . self::lang('smtp_error') . $lasterror['error']; if (!empty($lasterror['detail'])) { - $msg .= ' ' . $this->lang('smtp_detail') . $lasterror['detail']; + $msg .= ' ' . self::lang('smtp_detail') . $lasterror['detail']; } if (!empty($lasterror['smtp_code'])) { - $msg .= ' ' . $this->lang('smtp_code') . $lasterror['smtp_code']; + $msg .= ' ' . self::lang('smtp_code') . $lasterror['smtp_code']; } if (!empty($lasterror['smtp_code_ex'])) { - $msg .= ' ' . $this->lang('smtp_code_ex') . $lasterror['smtp_code_ex']; + $msg .= ' ' . self::lang('smtp_code_ex') . $lasterror['smtp_code_ex']; } } } @@ -4388,21 +4486,21 @@ class PHPMailer * * @return string */ - protected function lang($key) + protected static function lang($key) { - if (count($this->language) < 1) { - $this->setLanguage(); //Set the default language + if (count(self::$language) < 1) { + self::setLanguage(); //Set the default language } - if (array_key_exists($key, $this->language)) { + if (array_key_exists($key, self::$language)) { if ('smtp_connect_failed' === $key) { //Include a link to troubleshooting docs on SMTP connection failure. //This is by far the biggest cause of support questions //but it's usually not PHPMailer's fault. - return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; + return self::$language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; } - return $this->language[$key]; + return self::$language[$key]; } //Return the key as a fallback @@ -4417,7 +4515,7 @@ class PHPMailer */ private function getSmtpErrorMessage($base_key) { - $message = $this->lang($base_key); + $message = self::lang($base_key); $error = $this->smtp->getError(); if (!empty($error['error'])) { $message .= ' ' . $error['error']; @@ -4461,7 +4559,7 @@ class PHPMailer //Ensure name is not empty, and that neither name nor value contain line breaks if (empty($name) || strpbrk($name . $value, "\r\n") !== false) { if ($this->exceptions) { - throw new Exception($this->lang('invalid_header')); + throw new Exception(self::lang('invalid_header')); } return false; @@ -4854,7 +4952,7 @@ class PHPMailer return true; } - $this->setError($this->lang('variable_set') . $name); + $this->setError(self::lang('variable_set') . $name); return false; } @@ -4992,7 +5090,7 @@ class PHPMailer { if (!defined('PKCS7_TEXT')) { if ($this->exceptions) { - throw new Exception($this->lang('extension_missing') . 'openssl'); + throw new Exception(self::lang('extension_missing') . 'openssl'); } return ''; diff --git a/vendor/phpmailer/phpmailer/src/POP3.php b/vendor/phpmailer/phpmailer/src/POP3.php index 4d3e8db2..2c2cf789 100644 --- a/vendor/phpmailer/phpmailer/src/POP3.php +++ b/vendor/phpmailer/phpmailer/src/POP3.php @@ -46,7 +46,7 @@ class POP3 * * @var string */ - const VERSION = '6.12.0'; + const VERSION = '6.11.1'; /** * Default POP3 port number. diff --git a/vendor/phpmailer/phpmailer/src/SMTP.php b/vendor/phpmailer/phpmailer/src/SMTP.php index d8749c5b..3772c94a 100644 --- a/vendor/phpmailer/phpmailer/src/SMTP.php +++ b/vendor/phpmailer/phpmailer/src/SMTP.php @@ -35,7 +35,7 @@ class SMTP * * @var string */ - const VERSION = '6.12.0'; + const VERSION = '6.11.1'; /** * SMTP line break constant. @@ -205,6 +205,7 @@ class SMTP 'Haraka' => '/[\d]{3} Message Queued \((.*)\)/', 'ZoneMTA' => '/[\d]{3} Message queued as (.*)/', 'Mailjet' => '/[\d]{3} OK queued as (.*)/', + 'Gsmtp' => '/[\d]{3} 2\.0\.0 OK (.*) - gsmtp/', ]; /** @@ -633,10 +634,41 @@ class SMTP return false; } $oauth = $OAuth->getOauth64(); - - //Start authentication - if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { - return false; + /* + * An SMTP command line can have a maximum length of 512 bytes, including the command name, + * so the base64-encoded OAUTH token has a maximum length of: + * 512 - 13 (AUTH XOAUTH2) - 2 (CRLF) = 497 bytes + * If the token is longer than that, the command and the token must be sent separately as described in + * https://www.rfc-editor.org/rfc/rfc4954#section-4 + */ + if ($oauth === '') { + //Sending an empty auth token is legitimate, but it must be encoded as '=' + //to indicate it's not a 2-part command + if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 =', 235)) { + return false; + } + } elseif (strlen($oauth) <= 497) { + //Authenticate using a token in the initial-response part + if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { + return false; + } + } else { + //The token is too long, so we need to send it in two parts. + //Send the auth command without a token and expect a 334 + if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2', 334)) { + return false; + } + //Send the token + if (!$this->sendCommand('OAuth TOKEN', $oauth, [235, 334])) { + return false; + } + //If the server answers with 334, send an empty line and wait for a 235 + if ( + substr($this->last_reply, 0, 3) === '334' + && $this->sendCommand('AUTH End', '', 235) + ) { + return false; + } } break; default: @@ -1309,7 +1341,16 @@ class SMTP //stream_select returns false when the `select` system call is interrupted //by an incoming signal, try the select again - if (stripos($message, 'interrupted system call') !== false) { + if ( + stripos($message, 'interrupted system call') !== false || + ( + // on applications with a different locale than english, the message above is not found because + // it's translated. So we also check for the SOCKET_EINTR constant which is defined under + // Windows and UNIX-like platforms (if available on the platform). + defined('SOCKET_EINTR') && + stripos($message, 'stream_select(): Unable to select [' . SOCKET_EINTR . ']') !== false + ) + ) { $this->edebug( 'SMTP -> get_lines(): retrying stream_select', self::DEBUG_LOWLEVEL diff --git a/vendor/phpoffice/math/.github/workflows/php.yml b/vendor/phpoffice/math/.github/workflows/php.yml index 5390049e..df807baa 100644 --- a/vendor/phpoffice/math/.github/workflows/php.yml +++ b/vendor/phpoffice/math/.github/workflows/php.yml @@ -45,7 +45,6 @@ jobs: - '8.1' - '8.2' - '8.3' - - '8.4' steps: - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -76,7 +75,6 @@ jobs: - '8.1' - '8.2' - '8.3' - - '8.4' steps: - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/vendor/phpoffice/math/docs/index.md b/vendor/phpoffice/math/docs/index.md index 80b4333c..340b0dcc 100644 --- a/vendor/phpoffice/math/docs/index.md +++ b/vendor/phpoffice/math/docs/index.md @@ -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 | :material-check: | | +| | Semantics | | | ## Contributing diff --git a/vendor/phpoffice/math/mkdocs.yml b/vendor/phpoffice/math/mkdocs.yml index 198c5fe5..972d2741 100644 --- a/vendor/phpoffice/math/mkdocs.yml +++ b/vendor/phpoffice/math/mkdocs.yml @@ -57,9 +57,7 @@ nav: - Writers: 'usage/writers.md' - Credits: 'credits.md' - Releases: - - '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' + - '0.1.0 (WIP)': 'changes/0.1.0.md' - Developers: - 'Coveralls': 'https://coveralls.io/github/PHPOffice/Math' - 'Code Coverage': 'coverage/index.html' diff --git a/vendor/phpoffice/math/src/Math/Reader/MathML.php b/vendor/phpoffice/math/src/Math/Reader/MathML.php index cf1edb8d..58d93935 100644 --- a/vendor/phpoffice/math/src/Math/Reader/MathML.php +++ b/vendor/phpoffice/math/src/Math/Reader/MathML.php @@ -10,7 +10,6 @@ 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 { @@ -23,17 +22,8 @@ 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( [ '⁢', @@ -45,7 +35,7 @@ class MathML implements ReaderInterface ); $this->dom = new DOMDocument(); - $this->dom->loadXML($content); + $this->dom->loadXML($content, LIBXML_DTDLOAD); $this->math = new Math(); $this->parseNode(null, $this->math); diff --git a/vendor/phpoffice/math/src/Math/Writer/MathML.php b/vendor/phpoffice/math/src/Math/Writer/MathML.php index 34a20dc3..d8905f00 100644 --- a/vendor/phpoffice/math/src/Math/Writer/MathML.php +++ b/vendor/phpoffice/math/src/Math/Writer/MathML.php @@ -40,26 +40,6 @@ 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); @@ -141,9 +121,6 @@ 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', diff --git a/vendor/phpoffice/math/tests/Math/Reader/MathMLTest.php b/vendor/phpoffice/math/tests/Math/Reader/MathMLTest.php index a17c179c..db9b174a 100644 --- a/vendor/phpoffice/math/tests/Math/Reader/MathMLTest.php +++ b/vendor/phpoffice/math/tests/Math/Reader/MathMLTest.php @@ -7,7 +7,6 @@ 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; @@ -295,15 +294,4 @@ 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 = ' M'; - - $reader = new MathML(); - $math = $reader->read($content); - } } diff --git a/vendor/phpoffice/math/tests/Math/Writer/MathMLTest.php b/vendor/phpoffice/math/tests/Math/Writer/MathMLTest.php index 90724856..06aac383 100644 --- a/vendor/phpoffice/math/tests/Math/Writer/MathMLTest.php +++ b/vendor/phpoffice/math/tests/Math/Writer/MathMLTest.php @@ -80,35 +80,6 @@ class MathMLTest extends WriterTestCase $this->assertIsSchemaMathMLValid($output); } - public function testWriteSemantics(): void - { - $opTimes = new Element\Operator('⁢'); - - $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 = '' - . PHP_EOL - . '' - . '' - . '' - . 'y' - . ' y ' - . '' - . '' - . PHP_EOL; - $this->assertEquals($expected, $output); - $this->assertIsSchemaMathMLValid($output); - } - public function testWriteNotImplemented(): void { $this->expectException(NotImplementedException::class); diff --git a/vendor/phpoffice/phpword/LICENSE b/vendor/phpoffice/phpword/LICENSE index aebd12b0..8a1acaea 100644 --- a/vendor/phpoffice/phpword/LICENSE +++ b/vendor/phpoffice/phpword/LICENSE @@ -1,6 +1,6 @@ PHPWord, a pure PHP library for reading and writing word processing documents. -Copyright (c) 2010-2025 PHPWord. +Copyright (c) 2010-2016 PHPWord. PHPWord is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3 as published by diff --git a/vendor/phpoffice/phpword/README.md b/vendor/phpoffice/phpword/README.md index e080d32d..11bcd152 100644 --- a/vendor/phpoffice/phpword/README.md +++ b/vendor/phpoffice/phpword/README.md @@ -1,11 +1,11 @@ # ![PHPWord](https://rawgit.com/PHPOffice/PHPWord/develop/docs/images/phpword.svg "PHPWord") -[![Latest Stable Version](https://poser.pugx.org/phpoffice/phpword/v)](https://packagist.org/packages/phpoffice/phpword) +[![Latest Stable Version](https://poser.pugx.org/phpoffice/phpword/v/stable.png)](https://packagist.org/packages/phpoffice/phpword) [![Coverage Status](https://coveralls.io/repos/github/PHPOffice/PHPWord/badge.svg?branch=master)](https://coveralls.io/github/PHPOffice/PHPWord?branch=master) -[![Total Downloads](https://poser.pugx.org/phpoffice/phpword/downloads)](https://packagist.org/packages/phpoffice/phpword) -[![License](https://poser.pugx.org/phpoffice/phpword/license)](https://packagist.org/packages/phpoffice/phpword) - -Branch Master : [![PHPWord](https://github.com/PHPOffice/PHPWord/actions/workflows/php.yml/badge.svg?branch=master)](https://github.com/PHPOffice/PHPWord/actions/workflows/php.yml) +[![Total Downloads](https://poser.pugx.org/phpoffice/phpword/downloads.png)](https://packagist.org/packages/phpoffice/phpword) +[![License](https://poser.pugx.org/phpoffice/phpword/license.png)](https://packagist.org/packages/phpoffice/phpword) +[![CI](https://github.com/PHPOffice/PHPWord/actions/workflows/ci.yml/badge.svg)](https://github.com/PHPOffice/PHPWord/actions/workflows/ci.yml) +[![Join the chat at https://gitter.im/PHPOffice/PHPWord](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/PHPOffice/PHPWord) PHPWord is a library written in pure PHP that provides a set of classes to write to and read from different document file formats. The current version of PHPWord supports Microsoft [Office Open XML](http://en.wikipedia.org/wiki/Office_Open_XML) (OOXML or OpenXML), OASIS [Open Document Format for Office Applications](http://en.wikipedia.org/wiki/OpenDocument) (OpenDocument or ODF), [Rich Text Format](http://en.wikipedia.org/wiki/Rich_Text_Format) (RTF), HTML, and PDF. @@ -81,6 +81,7 @@ The following is a basic usage example of the PHPWord library. ```php =7.0", + "tecnickcom/tcpdf": "^6.5", "symfony/process": "^4.4 || ^5.0", - "tecnickcom/tcpdf": "^6.5" + "friendsofphp/php-cs-fixer": "^3.3", + "phpstan/phpstan-phpunit": "@stable" }, "suggest": { + "ext-zip": "Allows writing OOXML and ODF", + "ext-gd2": "Allows adding images", "ext-xmlwriter": "Allows writing OOXML and ODF", "ext-xsl": "Allows applying XSL style sheet to headers, to main document part, and to footers of an OOXML template", "dompdf/dompdf": "Allows writing PDF" diff --git a/vendor/phpoffice/phpword/mkdocs.yml b/vendor/phpoffice/phpword/mkdocs.yml index 97c84211..dfabbb19 100644 --- a/vendor/phpoffice/phpword/mkdocs.yml +++ b/vendor/phpoffice/phpword/mkdocs.yml @@ -63,7 +63,6 @@ nav: - OLE Object: 'usage/elements/oleobject.md' - Page Break: 'usage/elements/pagebreak.md' - Preserve Text: 'usage/elements/preservetext.md' - - Ruby: 'usage/elements/ruby.md' - Text: 'usage/elements/text.md' - TextBox: 'usage/elements/textbox.md' - Text Break: 'usage/elements/textbreak.md' @@ -88,9 +87,7 @@ nav: - Credits: 'credits.md' - Releases: - '1.x': - - '1.5.0 (WIP)': 'changes/1.x/1.5.0.md' - - '1.4.0': 'changes/1.x/1.4.0.md' - - '1.3.0': 'changes/1.x/1.3.0.md' + - '1.3.0 (WIP)': 'changes/1.x/1.3.0.md' - '1.2.0': 'changes/1.x/1.2.0.md' - '1.1.0': 'changes/1.x/1.1.0.md' - '1.0.0': 'changes/1.x/1.0.0.md' diff --git a/vendor/phpoffice/phpword/phpstan-baseline.neon b/vendor/phpoffice/phpword/phpstan-baseline.neon index 86a54276..909718b8 100644 --- a/vendor/phpoffice/phpword/phpstan-baseline.neon +++ b/vendor/phpoffice/phpword/phpstan-baseline.neon @@ -375,6 +375,11 @@ parameters: count: 1 path: src/PhpWord/Shared/Drawing.php + - + message: "#^Binary operation \"\\*\" between string and 50 results in an error\\.$#" + count: 1 + path: src/PhpWord/Shared/Html.php + - message: "#^Call to an undefined method DOMNode\\:\\:getAttribute\\(\\)\\.$#" count: 1 @@ -430,11 +435,6 @@ parameters: count: 1 path: src/PhpWord/Shared/Html.php - - - message: "#^Method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:parseRuby\\(\\) has no return type specified\\.$#" - count: 1 - path: src/PhpWord/Shared/Html.php - - message: "#^Method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:parseStyleDeclarations\\(\\) has no return type specified\\.$#" count: 1 @@ -447,7 +447,7 @@ parameters: - message: "#^Parameter \\#1 \\$attribute of static method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:parseStyle\\(\\) expects DOMAttr, DOMNode given\\.$#" - count: 3 + count: 1 path: src/PhpWord/Shared/Html.php - @@ -685,6 +685,11 @@ parameters: count: 1 path: src/PhpWord/Style/Cell.php + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Cell\\:\\:\\$shading is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Cell.php + - message: "#^Method PhpOffice\\\\PhpWord\\\\Style\\\\Chart\\:\\:getMajorTickPosition\\(\\) has no return type specified\\.$#" count: 1 @@ -755,6 +760,41 @@ parameters: count: 1 path: src/PhpWord/Style/Font.php + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Font\\:\\:\\$allCaps is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Font.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Font\\:\\:\\$doubleStrikethrough is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Font.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Font\\:\\:\\$lang is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Font.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Font\\:\\:\\$paragraph is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Font.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Font\\:\\:\\$shading is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Font.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Font\\:\\:\\$smallCaps is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Font.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Font\\:\\:\\$strikethrough is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Font.php + - message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Line\\:\\:\\$weight \\(int\\) does not accept float\\|int\\|null\\.$#" count: 1 @@ -775,6 +815,21 @@ parameters: count: 1 path: src/PhpWord/Style/Paragraph.php + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Paragraph\\:\\:\\$indentation is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Paragraph.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Paragraph\\:\\:\\$shading is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Paragraph.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Paragraph\\:\\:\\$spacing is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Paragraph.php + - message: "#^Result of && is always false\\.$#" count: 1 @@ -785,6 +840,36 @@ parameters: count: 1 path: src/PhpWord/Style/Section.php + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Section\\:\\:\\$lineNumbering is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Section.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Shape\\:\\:\\$extrusion is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Shape.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Shape\\:\\:\\$fill is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Shape.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Shape\\:\\:\\$frame is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Shape.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Shape\\:\\:\\$outline is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Shape.php + + - + message: "#^Property PhpOffice\\\\PhpWord\\\\Style\\\\Shape\\:\\:\\$shadow is never written, only read\\.$#" + count: 1 + path: src/PhpWord/Style/Shape.php + - message: "#^Method PhpOffice\\\\PhpWord\\\\Style\\\\TOC\\:\\:setTabLeader\\(\\) should return PhpOffice\\\\PhpWord\\\\Style\\\\TOC but returns PhpOffice\\\\PhpWord\\\\Style\\\\Tab\\.$#" count: 1 @@ -1035,6 +1120,11 @@ parameters: count: 1 path: src/PhpWord/Writer/AbstractWriter.php + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\PhpOffice\\\\PhpWord\\\\PhpWord\\)\\: Unexpected token \"\\\\n \\*\", expected variable at offset 78$#" + count: 1 + path: src/PhpWord/Writer/AbstractWriter.php + - message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\HTML\\\\Element\\\\AbstractElement\\:\\:write\\(\\) has no return type specified\\.$#" count: 1 @@ -1056,14 +1146,14 @@ parameters: path: src/PhpWord/Writer/ODText/Element/Table.php - - message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\ODText\\\\Element\\\\AbstractElement\\:\\:replaceTabs\\(\\) has parameter \\$text with no type specified\\.$#" + message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\ODText\\\\Element\\\\Text\\:\\:replacetabs\\(\\) has parameter \\$text with no type specified\\.$#" count: 1 - path: src/PhpWord/Writer/ODText/Element/AbstractElement.php + path: src/PhpWord/Writer/ODText/Element/Text.php - - message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\ODText\\\\Element\\\\AbstractElement\\:\\:replaceTabs\\(\\) has parameter \\$xmlWriter with no type specified\\.$#" + message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\ODText\\\\Element\\\\Text\\:\\:replacetabs\\(\\) has parameter \\$xmlWriter with no type specified\\.$#" count: 1 - path: src/PhpWord/Writer/ODText/Element/AbstractElement.php + path: src/PhpWord/Writer/ODText/Element/Text.php - message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\ODText\\\\Element\\\\Text\\:\\:writeChangeInsertion\\(\\) has parameter \\$start with no type specified\\.$#" @@ -1225,6 +1315,11 @@ parameters: count: 1 path: src/PhpWord/Writer/RTF/Style/Border.php + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\PhpOffice\\\\PhpWord\\\\PhpWord\\)\\: Unexpected token \"\\\\n \", expected variable at offset 86$#" + count: 1 + path: src/PhpWord/Writer/Word2007.php + - message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\Word2007\\\\Element\\\\AbstractElement\\:\\:write\\(\\) has no return type specified\\.$#" count: 1 @@ -1331,29 +1426,29 @@ parameters: path: tests/PhpWordTests/AbstractTestReader.php - - message: "#^Method PhpOffice\\\\PhpWordTests\\\\AbstractWebServerEmbedded\\:\\:getBaseUrl\\(\\) has no return type specified\\.$#" + message: "#^Method PhpOffice\\\\PhpWordTests\\\\AbstractWebServerEmbeddedTest\\:\\:getBaseUrl\\(\\) has no return type specified\\.$#" count: 1 - path: tests/PhpWordTests/AbstractWebServerEmbedded.php + path: tests/PhpWordTests/AbstractWebServerEmbeddedTest.php - - message: "#^Method PhpOffice\\\\PhpWordTests\\\\AbstractWebServerEmbedded\\:\\:getRemoteBmpImageUrl\\(\\) has no return type specified\\.$#" + message: "#^Method PhpOffice\\\\PhpWordTests\\\\AbstractWebServerEmbeddedTest\\:\\:getRemoteBmpImageUrl\\(\\) has no return type specified\\.$#" count: 1 - path: tests/PhpWordTests/AbstractWebServerEmbedded.php + path: tests/PhpWordTests/AbstractWebServerEmbeddedTest.php - - message: "#^Method PhpOffice\\\\PhpWordTests\\\\AbstractWebServerEmbedded\\:\\:getRemoteGifImageUrl\\(\\) has no return type specified\\.$#" + message: "#^Method PhpOffice\\\\PhpWordTests\\\\AbstractWebServerEmbeddedTest\\:\\:getRemoteGifImageUrl\\(\\) has no return type specified\\.$#" count: 1 - path: tests/PhpWordTests/AbstractWebServerEmbedded.php + path: tests/PhpWordTests/AbstractWebServerEmbeddedTest.php - - message: "#^Method PhpOffice\\\\PhpWordTests\\\\AbstractWebServerEmbedded\\:\\:getRemoteImageUrl\\(\\) has no return type specified\\.$#" + message: "#^Method PhpOffice\\\\PhpWordTests\\\\AbstractWebServerEmbeddedTest\\:\\:getRemoteImageUrl\\(\\) has no return type specified\\.$#" count: 1 - path: tests/PhpWordTests/AbstractWebServerEmbedded.php + path: tests/PhpWordTests/AbstractWebServerEmbeddedTest.php - - message: "#^Property PhpOffice\\\\PhpWordTests\\\\AbstractWebServerEmbedded\\:\\:\\$httpServer has no type specified\\.$#" + message: "#^Property PhpOffice\\\\PhpWordTests\\\\AbstractWebServerEmbeddedTest\\:\\:\\$httpServer has no type specified\\.$#" count: 1 - path: tests/PhpWordTests/AbstractWebServerEmbedded.php + path: tests/PhpWordTests/AbstractWebServerEmbeddedTest.php - message: "#^Parameter \\#1 \\$width of class PhpOffice\\\\PhpWord\\\\Element\\\\Cell constructor expects int\\|null, string given\\.$#" @@ -1694,11 +1789,6 @@ parameters: message: "#^Cannot access property \\$length on DOMNodeList\\\\|false\\.$#" count: 9 path: tests/PhpWordTests/Writer/HTML/ElementTest.php - - - - message: "#^Cannot access property \\$length on DOMNodeList\\\\|false\\.$#" - count: 2 - path: tests/PhpWordTests/Writer/HTML/Element/RubyTest.php - message: "#^Cannot call method item\\(\\) on DOMNodeList\\\\|false\\.$#" diff --git a/vendor/phpoffice/phpword/phpword.ini.dist b/vendor/phpoffice/phpword/phpword.ini.dist index 21d3b506..f3f66dbe 100644 --- a/vendor/phpoffice/phpword/phpword.ini.dist +++ b/vendor/phpoffice/phpword/phpword.ini.dist @@ -14,7 +14,6 @@ outputEscapingEnabled = false defaultFontName = Arial defaultFontSize = 10 -defaultFontColor = 000000 [Paper] diff --git a/vendor/phpoffice/phpword/src/PhpWord/Collection/AbstractCollection.php b/vendor/phpoffice/phpword/src/PhpWord/Collection/AbstractCollection.php index 97c2c45d..646489d8 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Collection/AbstractCollection.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Collection/AbstractCollection.php @@ -1,5 +1,4 @@ */ class Bookmarks extends AbstractCollection diff --git a/vendor/phpoffice/phpword/src/PhpWord/Collection/Charts.php b/vendor/phpoffice/phpword/src/PhpWord/Collection/Charts.php index ef1c6597..7c2dfbab 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Collection/Charts.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Collection/Charts.php @@ -1,5 +1,4 @@ */ class Charts extends AbstractCollection diff --git a/vendor/phpoffice/phpword/src/PhpWord/Collection/Comments.php b/vendor/phpoffice/phpword/src/PhpWord/Collection/Comments.php index 4f139435..5fa4020a 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Collection/Comments.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Collection/Comments.php @@ -1,5 +1,4 @@ */ class Comments extends AbstractCollection diff --git a/vendor/phpoffice/phpword/src/PhpWord/Collection/Endnotes.php b/vendor/phpoffice/phpword/src/PhpWord/Collection/Endnotes.php index 3de1ad22..09903b1b 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Collection/Endnotes.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Collection/Endnotes.php @@ -1,5 +1,4 @@ */ class Endnotes extends AbstractCollection diff --git a/vendor/phpoffice/phpword/src/PhpWord/Collection/Footnotes.php b/vendor/phpoffice/phpword/src/PhpWord/Collection/Footnotes.php index 804a45c8..0387fce3 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Collection/Footnotes.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Collection/Footnotes.php @@ -1,5 +1,4 @@ */ class Footnotes extends AbstractCollection diff --git a/vendor/phpoffice/phpword/src/PhpWord/Collection/Titles.php b/vendor/phpoffice/phpword/src/PhpWord/Collection/Titles.php index 1a943697..543aabda 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Collection/Titles.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Collection/Titles.php @@ -1,5 +1,4 @@ */ class Titles extends AbstractCollection diff --git a/vendor/phpoffice/phpword/src/PhpWord/ComplexType/FootnoteProperties.php b/vendor/phpoffice/phpword/src/PhpWord/ComplexType/FootnoteProperties.php index 10c426e8..2e7743ca 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/ComplexType/FootnoteProperties.php +++ b/vendor/phpoffice/phpword/src/PhpWord/ComplexType/FootnoteProperties.php @@ -1,5 +1,4 @@ newInstanceArgs($elementArgs); // Set parent container @@ -167,7 +165,7 @@ abstract class AbstractContainer extends AbstractElement /** * Get all elements. * - * @return AbstractElement[] + * @return \PhpOffice\PhpWord\Element\AbstractElement[] */ public function getElements() { @@ -179,7 +177,7 @@ abstract class AbstractContainer extends AbstractElement * * @param int $index * - * @return null|AbstractElement + * @return null|\PhpOffice\PhpWord\Element\AbstractElement */ public function getElement($index) { @@ -193,13 +191,13 @@ abstract class AbstractContainer extends AbstractElement /** * Removes the element at requested index. * - * @param AbstractElement|int $toRemove + * @param int|\PhpOffice\PhpWord\Element\AbstractElement $toRemove */ public function removeElement($toRemove): void { if (is_int($toRemove) && array_key_exists($toRemove, $this->elements)) { unset($this->elements[$toRemove]); - } elseif ($toRemove instanceof AbstractElement) { + } elseif ($toRemove instanceof \PhpOffice\PhpWord\Element\AbstractElement) { foreach ($this->elements as $key => $element) { if ($element->getElementId() === $toRemove->getElementId()) { unset($this->elements[$key]); @@ -255,7 +253,7 @@ abstract class AbstractContainer extends AbstractElement 'Footnote' => ['Section', 'TextRun', 'Cell', 'ListItemRun'], 'Endnote' => ['Section', 'TextRun', 'Cell'], 'PreserveText' => ['Section', 'Header', 'Footer', 'Cell'], - 'Title' => ['Section', 'Cell', 'Header'], + 'Title' => ['Section', 'Cell'], 'TOC' => ['Section'], 'PageBreak' => ['Section'], 'Chart' => ['Section', 'Cell'], diff --git a/vendor/phpoffice/phpword/src/PhpWord/Element/AbstractElement.php b/vendor/phpoffice/phpword/src/PhpWord/Element/AbstractElement.php index 3a29b686..385e4d31 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Element/AbstractElement.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Element/AbstractElement.php @@ -1,5 +1,4 @@ elementId = substr(md5((string) mt_rand()), 0, 6); + $this->elementId = substr(md5(mt_rand()), 0, 6); } /** @@ -290,6 +291,8 @@ abstract class AbstractElement /** * Get comments start. + * + * @return Comments */ public function getCommentsRangeStart(): ?Comments { @@ -298,6 +301,8 @@ abstract class AbstractElement /** * Get comment start. + * + * @return Comment */ public function getCommentRangeStart(): ?Comment { @@ -334,6 +339,8 @@ abstract class AbstractElement /** * Get comments end. + * + * @return Comments */ public function getCommentsRangeEnd(): ?Comments { @@ -342,6 +349,8 @@ abstract class AbstractElement /** * Get comment end. + * + * @return Comment */ public function getCommentRangeEnd(): ?Comment { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Element/Bookmark.php b/vendor/phpoffice/phpword/src/PhpWord/Element/Bookmark.php index 151d5a48..4fe3d0f0 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Element/Bookmark.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Element/Bookmark.php @@ -1,5 +1,4 @@ text; } @@ -108,7 +109,7 @@ class Link extends AbstractElement /** * Get Text style. * - * @return null|Font|string + * @return null|\PhpOffice\PhpWord\Style\Font|string */ public function getFontStyle() { @@ -118,7 +119,7 @@ class Link extends AbstractElement /** * Get Paragraph style. * - * @return null|Paragraph|string + * @return null|\PhpOffice\PhpWord\Style\Paragraph|string */ public function getParagraphStyle() { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Element/ListItem.php b/vendor/phpoffice/phpword/src/PhpWord/Element/ListItem.php index bedfd110..d3f25c29 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Element/ListItem.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Element/ListItem.php @@ -1,5 +1,4 @@ $collectionArray; if (in_array($type, [Header::AUTO, Header::FIRST, Header::EVEN])) { $index = count($collection); - /** @var AbstractContainer $container Type hint */ + /** @var \PhpOffice\PhpWord\Element\AbstractContainer $container Type hint */ $container = new $containerClass($this->sectionId, ++$index, $type); $container->setPhpWord($this->phpWord); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Element/Shape.php b/vendor/phpoffice/phpword/src/PhpWord/Element/Shape.php index 9ea221db..15161f89 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Element/Shape.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Element/Shape.php @@ -1,5 +1,4 @@ phpWord->getTitles()->getItems(); foreach ($titles as $i => $title) { - /** @var Title $title Type hint */ + /** @var \PhpOffice\PhpWord\Element\Title $title Type hint */ $depth = $title->getDepth(); if ($this->minDepth > $depth) { unset($titles[$i]); @@ -111,7 +110,7 @@ class TOC extends AbstractElement /** * Get TOC Style. * - * @return TOCStyle + * @return \PhpOffice\PhpWord\Style\TOC */ public function getStyleTOC() { @@ -121,7 +120,7 @@ class TOC extends AbstractElement /** * Get Font Style. * - * @return Font|string + * @return \PhpOffice\PhpWord\Style\Font|string */ public function getStyleFont() { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Element/Table.php b/vendor/phpoffice/phpword/src/PhpWord/Element/Table.php index 7fb10306..53a828a9 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Element/Table.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Element/Table.php @@ -1,5 +1,4 @@ rows); for ($i = 0; $i < $rowCount; ++$i) { - /** @var Row $row Type hint */ + /** @var \PhpOffice\PhpWord\Element\Row $row Type hint */ $row = $this->rows[$i]; $cellCount = count($row->getCells()); if ($columnCount < $cellCount) { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Element/Text.php b/vendor/phpoffice/phpword/src/PhpWord/Element/Text.php index f20b273e..96953953 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Element/Text.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Element/Text.php @@ -1,5 +1,4 @@ text; } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Element/TextBox.php b/vendor/phpoffice/phpword/src/PhpWord/Element/TextBox.php index b9f8b50b..af37f657 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Element/TextBox.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Element/TextBox.php @@ -1,5 +1,4 @@ getElements() as $element) { if ($element instanceof Text) { $outstr .= $element->getText(); - } elseif ($element instanceof Ruby) { - $outstr .= $element->getBaseTextRun()->getText() . - ' (' . $element->getRubyTextRun()->getText() . ')'; } } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Element/Title.php b/vendor/phpoffice/phpword/src/PhpWord/Element/Title.php index 89f438a5..8fd5b21b 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Element/Title.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Element/Title.php @@ -1,5 +1,4 @@ load($filename); @@ -101,7 +100,7 @@ abstract class IOFactory */ public static function extractVariables(string $filename, string $readerName = 'Word2007'): array { - /** @var ReaderInterface $reader */ + /** @var \PhpOffice\PhpWord\Reader\ReaderInterface $reader */ $reader = self::createReader($readerName); $document = $reader->load($filename); $extractedVariables = []; diff --git a/vendor/phpoffice/phpword/src/PhpWord/Media.php b/vendor/phpoffice/phpword/src/PhpWord/Media.php index 0a340a0a..31487a91 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Media.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Media.php @@ -1,5 +1,4 @@ setRels($relationships); $part->read($phpWord); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Reader/ODText/AbstractPart.php b/vendor/phpoffice/phpword/src/PhpWord/Reader/ODText/AbstractPart.php index 447c96f0..4c89192e 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Reader/ODText/AbstractPart.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Reader/ODText/AbstractPart.php @@ -1,5 +1,4 @@ getElements('w:r|w:hyperlink', $domNode); - $hasRubyElement = $xmlReader->elementExists('w:r/w:ruby', $domNode); - if ($nodes->length === 1 && !$hasRubyElement) { + if ($nodes->length === 1) { $textContent = htmlspecialchars($xmlReader->getValue('w:t', $nodes->item(0)), ENT_QUOTES, 'UTF-8'); } else { $textContent = new TextRun($paragraphStyle); @@ -455,7 +450,7 @@ abstract class AbstractPart /** * Read w:r. * - * @param AbstractContainer $parent + * @param \PhpOffice\PhpWord\Element\AbstractContainer $parent * @param string $docPart * @param mixed $paragraphStyle * @@ -589,47 +584,9 @@ abstract class AbstractPart } } elseif ($node->nodeName == 'w:softHyphen') { $element = $parent->addText("\u{200c}", $fontStyle, $paragraphStyle); - } elseif ($node->nodeName == 'w:ruby') { - $rubyPropertiesNode = $xmlReader->getElement('w:rubyPr', $node); - $properties = $this->readRubyProperties($xmlReader, $rubyPropertiesNode); - // read base text node - $baseText = new TextRun($paragraphStyle); - $baseTextNode = $xmlReader->getElement('w:rubyBase/w:r', $node); - $this->readRun($xmlReader, $baseTextNode, $baseText, $docPart, $paragraphStyle); - // read the actual ruby text (e.g. furigana in Japanese) - $rubyText = new TextRun($paragraphStyle); - $rubyTextNode = $xmlReader->getElement('w:rt/w:r', $node); - $this->readRun($xmlReader, $rubyTextNode, $rubyText, $docPart, $paragraphStyle); - // add element to parent - $parent->addRuby($baseText, $rubyText, $properties); } } - /** - * Read w:rubyPr element. - * - * @param XMLReader $xmlReader reader for XML - * @param DOMElement $domNode w:RubyPr element - * - * @return RubyProperties ruby properties from element - */ - protected function readRubyProperties(XMLReader $xmlReader, DOMElement $domNode): RubyProperties - { - $rubyAlignment = $xmlReader->getElement('w:rubyAlign', $domNode)->getAttribute('w:val'); - $rubyHps = $xmlReader->getElement('w:hps', $domNode)->getAttribute('w:val'); // font face - $rubyHpsRaise = $xmlReader->getElement('w:hpsRaise', $domNode)->getAttribute('w:val'); // pts above base text - $rubyHpsBaseText = $xmlReader->getElement('w:hpsBaseText', $domNode)->getAttribute('w:val'); // base text size - $rubyLid = $xmlReader->getElement('w:lid', $domNode)->getAttribute('w:val'); // type of ruby - $properties = new RubyProperties(); - $properties->setAlignment($rubyAlignment); - $properties->setFontFaceSize((float) $rubyHps); - $properties->setFontPointsAboveBaseText((float) $rubyHpsRaise); - $properties->setFontSizeForBaseText((float) $rubyHpsBaseText); - $properties->setLanguageId($rubyLid); - - return $properties; - } - /** * Read w:tbl. * @@ -704,11 +661,8 @@ abstract class AbstractPart 'alignment' => [self::READ_VALUE, 'w:jc'], 'basedOn' => [self::READ_VALUE, 'w:basedOn'], 'next' => [self::READ_VALUE, 'w:next'], - 'indentLeft' => [self::READ_VALUE, 'w:ind', 'w:left'], - 'indentRight' => [self::READ_VALUE, 'w:ind', 'w:right'], - 'indentHanging' => [self::READ_VALUE, 'w:ind', 'w:hanging'], - 'indentFirstLine' => [self::READ_VALUE, 'w:ind', 'w:firstLine'], - 'indentFirstLineChars' => [self::READ_VALUE, 'w:ind', 'w:firstLineChars'], + 'indent' => [self::READ_VALUE, 'w:ind', 'w:left'], + 'hanging' => [self::READ_VALUE, 'w:ind', 'w:hanging'], 'spaceAfter' => [self::READ_VALUE, 'w:spacing', 'w:after'], 'spaceBefore' => [self::READ_VALUE, 'w:spacing', 'w:before'], 'widowControl' => [self::READ_FALSE, 'w:widowControl'], diff --git a/vendor/phpoffice/phpword/src/PhpWord/Reader/Word2007/DocPropsApp.php b/vendor/phpoffice/phpword/src/PhpWord/Reader/Word2007/DocPropsApp.php index c7ecb007..9d6f3cbb 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Reader/Word2007/DocPropsApp.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Reader/Word2007/DocPropsApp.php @@ -1,5 +1,4 @@ setDefaultFontSize($fontDefaultStyle['size']); } - if (array_key_exists('color', $fontDefaultStyle)) { - $phpWord->setDefaultFontColor($fontDefaultStyle['color']); - } if (array_key_exists('lang', $fontDefaultStyle)) { $phpWord->getSettings()->setThemeFontLang(new Language($fontDefaultStyle['lang'])); } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Settings.php b/vendor/phpoffice/phpword/src/PhpWord/Settings.php index 16f49166..984486cc 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Settings.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Settings.php @@ -1,5 +1,4 @@ cssContent); preg_match_all('/(.+?)\s?\{\s?(.+?)\s?\}/', $cssContent, $cssExtracted); + // Check the number of extracted + if (count($cssExtracted) != 3) { + return; + } // Check if there are x selectors and x rules if (count($cssExtracted[1]) != count($cssExtracted[2])) { return; diff --git a/vendor/phpoffice/phpword/src/PhpWord/Shared/Drawing.php b/vendor/phpoffice/phpword/src/PhpWord/Shared/Drawing.php index 8af7da2f..df218108 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Shared/Drawing.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Shared/Drawing.php @@ -1,5 +1,4 @@ . * - * @param AbstractContainer $element Where the parts need to be added + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element Where the parts need to be added * @param string $html The code to parse * @param bool $fullHTML If it's a full HTML, no need to add 'body' tag * @param bool $preserveWhiteSpace If false, the whitespaces between nodes will be removed @@ -130,21 +127,21 @@ class Html break; case 'width': // tables, cells - $val = $val === 'auto' ? '100%' : $val; if (false !== strpos($val, '%')) { // e.g. or
$styles['width'] = (int) $val * 50; $styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::PERCENT; } else { // e.g. , where "2" = 2px (always pixels) - $styles['cellSpacing'] = Converter::pixelToTwip(self::convertHtmlSize($val)); + $val = (int) $val . 'px'; + $styles['cellSpacing'] = Converter::cssToTwip($val); break; case 'bgcolor': @@ -188,7 +185,7 @@ class Html * Parse a node and add a corresponding element to the parent element. * * @param DOMNode $node node to parse - * @param AbstractContainer $element object to add an element corresponding with the node + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element object to add an element corresponding with the node * @param array $styles Array with all styles * @param array $data Array to transport data to a next level in the DOM tree, for example level of listitems */ @@ -211,16 +208,16 @@ class Html // Node mapping table $nodes = [ - // $method $node $element $styles $data $argument1 $argument2 - 'p' => ['Paragraph', $node, $element, $styles, null, null, null], - 'h1' => ['Heading', $node, $element, $styles, null, 'Heading1', null], - 'h2' => ['Heading', $node, $element, $styles, null, 'Heading2', null], - 'h3' => ['Heading', $node, $element, $styles, null, 'Heading3', null], - 'h4' => ['Heading', $node, $element, $styles, null, 'Heading4', null], - 'h5' => ['Heading', $node, $element, $styles, null, 'Heading5', null], - 'h6' => ['Heading', $node, $element, $styles, null, 'Heading6', null], - '#text' => ['Text', $node, $element, $styles, null, null, null], - 'strong' => ['Property', null, null, $styles, null, 'bold', true], + // $method $node $element $styles $data $argument1 $argument2 + 'p' => ['Paragraph', $node, $element, $styles, null, null, null], + 'h1' => ['Heading', null, $element, $styles, null, 'Heading1', null], + 'h2' => ['Heading', null, $element, $styles, null, 'Heading2', null], + 'h3' => ['Heading', null, $element, $styles, null, 'Heading3', null], + 'h4' => ['Heading', null, $element, $styles, null, 'Heading4', null], + 'h5' => ['Heading', null, $element, $styles, null, 'Heading5', null], + 'h6' => ['Heading', null, $element, $styles, null, 'Heading6', null], + '#text' => ['Text', $node, $element, $styles, null, null, null], + 'strong' => ['Property', null, null, $styles, null, 'bold', true], 'b' => ['Property', null, null, $styles, null, 'bold', true], 'em' => ['Property', null, null, $styles, null, 'italic', true], 'i' => ['Property', null, null, $styles, null, 'italic', true], @@ -241,7 +238,6 @@ class Html 'a' => ['Link', $node, $element, $styles, null, null, null], 'input' => ['Input', $node, $element, $styles, null, null, null], 'hr' => ['HorizRule', $node, $element, $styles, null, null, null], - 'ruby' => ['Ruby', $node, $element, $styles, null, null, null], ]; $newElement = null; @@ -280,7 +276,7 @@ class Html * Parse child nodes. * * @param DOMNode $node - * @param AbstractContainer|Row|Table $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer|Row|Table $element * @param array $styles * @param array $data */ @@ -302,10 +298,10 @@ class Html * Parse paragraph node. * * @param DOMNode $node - * @param AbstractContainer $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array &$styles * - * @return \PhpOffice\PhpWord\Element\PageBreak|TextRun + * @return \PhpOffice\PhpWord\Element\PageBreak|\PhpOffice\PhpWord\Element\TextRun */ protected static function parseParagraph($node, $element, &$styles) { @@ -321,7 +317,7 @@ class Html * Parse input node. * * @param DOMNode $node - * @param AbstractContainer $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array &$styles */ protected static function parseInput($node, $element, &$styles): void @@ -345,25 +341,28 @@ class Html /** * Parse heading node. * + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element + * @param array &$styles * @param string $argument1 Name of heading style * + * @return \PhpOffice\PhpWord\Element\TextRun + * * @todo Think of a clever way of defining header styles, now it is only based on the assumption, that * Heading1 - Heading6 are already defined somewhere */ - protected static function parseHeading(DOMNode $node, AbstractContainer $element, array &$styles, string $argument1): TextRun + protected static function parseHeading($element, &$styles, $argument1) { - $style = new Paragraph(); - $style->setStyleName($argument1); - $style->setStyleByArray(self::parseInlineStyle($node, $styles['paragraph'])); + $styles['paragraph'] = $argument1; + $newElement = $element->addTextRun($styles['paragraph']); - return $element->addTextRun($style); + return $newElement; } /** * Parse text node. * * @param DOMNode $node - * @param AbstractContainer $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array &$styles */ protected static function parseText($node, $element, &$styles): void @@ -407,7 +406,7 @@ class Html * Parse table node. * * @param DOMNode $node - * @param AbstractContainer $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array &$styles * * @return Table $element @@ -438,7 +437,7 @@ class Html * Parse a table row. * * @param DOMNode $node - * @param Table $element + * @param \PhpOffice\PhpWord\Element\Table $element * @param array &$styles * * @return Row $element @@ -461,10 +460,10 @@ class Html * Parse table cell. * * @param DOMNode $node - * @param Table $element + * @param \PhpOffice\PhpWord\Element\Table $element * @param array &$styles * - * @return \PhpOffice\PhpWord\Element\Cell|TextRun $element + * @return \PhpOffice\PhpWord\Element\Cell|\PhpOffice\PhpWord\Element\TextRun $element */ protected static function parseCell($node, $element, &$styles) { @@ -555,7 +554,7 @@ class Html * Parse list node. * * @param DOMNode $node - * @param AbstractContainer $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array &$styles * @param array &$data */ @@ -628,15 +627,15 @@ class Html return [ 'type' => 'hybridMultilevel', 'levels' => [ - ['format' => NumberFormat::BULLET, 'text' => '•', 'alignment' => 'left', 'tabPos' => 720, 'left' => 720, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'], - ['format' => NumberFormat::BULLET, 'text' => '◦', 'alignment' => 'left', 'tabPos' => 1440, 'left' => 1440, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'], - ['format' => NumberFormat::BULLET, 'text' => '•', 'alignment' => 'left', 'tabPos' => 2160, 'left' => 2160, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'], - ['format' => NumberFormat::BULLET, 'text' => '•', 'alignment' => 'left', 'tabPos' => 2880, 'left' => 2880, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'], - ['format' => NumberFormat::BULLET, 'text' => '◦', 'alignment' => 'left', 'tabPos' => 3600, 'left' => 3600, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'], - ['format' => NumberFormat::BULLET, 'text' => '•', 'alignment' => 'left', 'tabPos' => 4320, 'left' => 4320, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'], - ['format' => NumberFormat::BULLET, 'text' => '•', 'alignment' => 'left', 'tabPos' => 5040, 'left' => 5040, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'], - ['format' => NumberFormat::BULLET, 'text' => '◦', 'alignment' => 'left', 'tabPos' => 5760, 'left' => 5760, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'], - ['format' => NumberFormat::BULLET, 'text' => '•', 'alignment' => 'left', 'tabPos' => 6480, 'left' => 6480, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'], + ['format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 720, 'left' => 720, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'], + ['format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 1440, 'left' => 1440, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'], + ['format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 2160, 'left' => 2160, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'], + ['format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 2880, 'left' => 2880, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'], + ['format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 3600, 'left' => 3600, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'], + ['format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 4320, 'left' => 4320, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'], + ['format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 5040, 'left' => 5040, 'hanging' => 360, 'font' => 'Symbol', 'hint' => 'default'], + ['format' => NumberFormat::BULLET, 'text' => 'o', 'alignment' => 'left', 'tabPos' => 5760, 'left' => 5760, 'hanging' => 360, 'font' => 'Courier New', 'hint' => 'default'], + ['format' => NumberFormat::BULLET, 'text' => '', 'alignment' => 'left', 'tabPos' => 6480, 'left' => 6480, 'hanging' => 360, 'font' => 'Wingdings', 'hint' => 'default'], ], ]; } @@ -645,7 +644,7 @@ class Html * Parse list item node. * * @param DOMNode $node - * @param AbstractContainer $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array &$styles * @param array $data * @@ -705,10 +704,6 @@ class Html case 'text-align': $styles['alignment'] = self::mapAlign($value, $bidi); - break; - case 'ruby-align': - $styles['rubyAlignment'] = self::mapRubyAlign($value); - break; case 'display': $styles['hidden'] = $value === 'none' || $value === 'hidden'; @@ -738,7 +733,7 @@ class Html break; case 'line-height': $matches = []; - if ($value === 'normal' || $value === 'inherit') { + if ($value === 'normal') { $spacingLineRule = \PhpOffice\PhpWord\SimpleType\LineSpacingRule::AUTO; $spacing = 0; } elseif (preg_match('/([0-9]+\.?[0-9]*[a-z]+)/', $value, $matches)) { @@ -808,58 +803,6 @@ class Html $styles['spaceAfter'] = Converter::cssToTwip($value); break; - - case 'padding': - $valueTop = $valueRight = $valueBottom = $valueLeft = null; - $cValue = preg_replace('# +#', ' ', trim($value)); - $paddingArr = explode(' ', $cValue); - $countParams = count($paddingArr); - if ($countParams == 1) { - $valueTop = $valueRight = $valueBottom = $valueLeft = $paddingArr[0]; - } elseif ($countParams == 2) { - $valueTop = $valueBottom = $paddingArr[0]; - $valueRight = $valueLeft = $paddingArr[1]; - } elseif ($countParams == 3) { - $valueTop = $paddingArr[0]; - $valueRight = $valueLeft = $paddingArr[1]; - $valueBottom = $paddingArr[2]; - } elseif ($countParams == 4) { - $valueTop = $paddingArr[0]; - $valueRight = $paddingArr[1]; - $valueBottom = $paddingArr[2]; - $valueLeft = $paddingArr[3]; - } - if ($valueTop !== null) { - $styles['paddingTop'] = Converter::cssToTwip($valueTop); - } - if ($valueRight !== null) { - $styles['paddingRight'] = Converter::cssToTwip($valueRight); - } - if ($valueBottom !== null) { - $styles['paddingBottom'] = Converter::cssToTwip($valueBottom); - } - if ($valueLeft !== null) { - $styles['paddingLeft'] = Converter::cssToTwip($valueLeft); - } - - break; - case 'padding-top': - $styles['paddingTop'] = Converter::cssToTwip($value); - - break; - case 'padding-right': - $styles['paddingRight'] = Converter::cssToTwip($value); - - break; - case 'padding-bottom': - $styles['paddingBottom'] = Converter::cssToTwip($value); - - break; - case 'padding-left': - $styles['paddingLeft'] = Converter::cssToTwip($value); - - break; - case 'border-color': self::mapBorderColor($styles, $value); @@ -943,7 +886,7 @@ class Html * Parse image node. * * @param DOMNode $node - * @param AbstractContainer $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * * @return \PhpOffice\PhpWord\Element\Image */ @@ -958,12 +901,36 @@ class Html break; case 'width': - $style['width'] = self::convertHtmlSize($attribute->value); + $width = $attribute->value; + + // pt + if (false !== strpos($width, 'pt')) { + $width = Converter::pointToPixel((float) str_replace('pt', '', $width)); + } + + // px + if (false !== strpos($width, 'px')) { + $width = str_replace('px', '', $width); + } + + $style['width'] = $width; $style['unit'] = \PhpOffice\PhpWord\Style\Image::UNIT_PX; break; case 'height': - $style['height'] = self::convertHtmlSize($attribute->value); + $height = $attribute->value; + + // pt + if (false !== strpos($height, 'pt')) { + $height = Converter::pointToPixel((float) str_replace('pt', '', $height)); + } + + // px + if (false !== strpos($height, 'px')) { + $height = str_replace('px', '', $height); + } + + $style['height'] = $height; $style['unit'] = \PhpOffice\PhpWord\Style\Image::UNIT_PX; break; @@ -1003,15 +970,14 @@ class Html $match = []; preg_match('/data:image\/(\w+);base64,(.+)/', $src, $match); - if (!empty($match)) { - $src = $imgFile = $tmpDir . uniqid() . '.' . $match[1]; - $ifp = fopen($imgFile, 'wb'); + $src = $imgFile = $tmpDir . uniqid() . '.' . $match[1]; - if ($ifp !== false) { - fwrite($ifp, base64_decode($match[2])); - fclose($ifp); - } + $ifp = fopen($imgFile, 'wb'); + + if ($ifp !== false) { + fwrite($ifp, base64_decode($match[2])); + fclose($ifp); } } $src = urldecode($src); @@ -1107,23 +1073,6 @@ class Html } } - /** - * Transforms a HTML/CSS ruby alignment into a \PhpOffice\PhpWord\SimpleType\Jc. - */ - protected static function mapRubyAlign(string $cssRubyAlignment): string - { - switch ($cssRubyAlignment) { - case 'center': - return RubyProperties::ALIGNMENT_CENTER; - case 'start': - return RubyProperties::ALIGNMENT_LEFT; - case 'space-between': - return RubyProperties::ALIGNMENT_DISTRIBUTE_SPACE; - default: - return ''; - } - } - /** * Transforms a HTML/CSS vertical alignment. * @@ -1180,7 +1129,7 @@ class Html /** * Parse line break. * - * @param AbstractContainer $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element */ protected static function parseLineBreak($element): void { @@ -1191,7 +1140,7 @@ class Html * Parse link node. * * @param DOMNode $node - * @param AbstractContainer $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element * @param array $styles */ protected static function parseLink($node, $element, &$styles) @@ -1223,7 +1172,7 @@ class Html * Note: Word rule is not the same as HTML's
since it does not support width and thus neither alignment. * * @param DOMNode $node - * @param AbstractContainer $element + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element */ protected static function parseHorizRule($node, $element): void { @@ -1252,59 +1201,6 @@ class Html // - repeated text, e.g. underline "_", because of unpredictable line wrapping } - /** - * Parse ruby node. - * - * @param DOMNode $node - * @param AbstractContainer $element - * @param array $styles - */ - protected static function parseRuby($node, $element, &$styles) - { - $rubyProperties = new RubyProperties(); - $baseTextRun = new TextRun($styles['paragraph']); - $rubyTextRun = new TextRun(null); - if ($node->hasAttributes()) { - $langAttr = $node->attributes->getNamedItem('lang'); - if ($langAttr !== null) { - $rubyProperties->setLanguageId($langAttr->textContent); - } - $styleAttr = $node->attributes->getNamedItem('style'); - if ($styleAttr !== null) { - $styles = self::parseStyle($styleAttr, $styles['paragraph']); - if (isset($styles['rubyAlignment']) && $styles['rubyAlignment'] !== '') { - $rubyProperties->setAlignment($styles['rubyAlignment']); - } - if (isset($styles['size']) && $styles['size'] !== '') { - $rubyProperties->setFontSizeForBaseText($styles['size']); - } - $baseTextRun->setParagraphStyle($styles); - } - } - foreach ($node->childNodes as $child) { - if ($child->nodeName === '#text') { - $content = trim($child->textContent); - if ($content !== '') { - $baseTextRun->addText($content); - } - } elseif ($child->nodeName === 'rt') { - $rubyTextRun->addText(trim($child->textContent)); - if ($child->hasAttributes()) { - $styleAttr = $child->attributes->getNamedItem('style'); - if ($styleAttr !== null) { - $styles = self::parseStyle($styleAttr, []); - if (isset($styles['size']) && $styles['size'] !== '') { - $rubyProperties->setFontFaceSize($styles['size']); - } - $rubyTextRun->setParagraphStyle($styles); - } - } - } - } - - return $element->addRuby($baseTextRun, $rubyTextRun, $rubyProperties); - } - private static function convertRgb(string $rgb): string { if (preg_match(self::RGB_REGEXP, $rgb, $matches) === 1) { @@ -1313,22 +1209,4 @@ class Html return trim($rgb, '# '); } - - /** - * Transform HTML sizes (pt, px) in pixels. - */ - protected static function convertHtmlSize(string $size): float - { - // pt - if (false !== strpos($size, 'pt')) { - return Converter::pointToPixel((float) str_replace('pt', '', $size)); - } - - // px - if (false !== strpos($size, 'px')) { - return (float) str_replace('px', '', $size); - } - - return (float) $size; - } } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Shared/Microsoft/PasswordEncoder.php b/vendor/phpoffice/phpword/src/PhpWord/Shared/Microsoft/PasswordEncoder.php index 4762cc71..d6cf69fc 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Shared/Microsoft/PasswordEncoder.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Shared/Microsoft/PasswordEncoder.php @@ -1,5 +1,4 @@ tempDir . DIRECTORY_SEPARATOR . $filenameParts['basename'], 'wb'); - if ($handle) { - fwrite($handle, $contents); - fclose($handle); - } + fwrite($handle, $contents); + fclose($handle); // Add temp file to zip $filename = $this->tempDir . DIRECTORY_SEPARATOR . $filenameParts['basename']; @@ -423,15 +420,4 @@ class ZipArchive return ($listIndex > -1) ? $listIndex : false; } - - /** - * Add an empty directory to the zip archive (emulate \ZipArchive). - * - * @param string $dirname Directory name to add to the zip archive - */ - public function addEmptyDir(string $dirname): bool - { - // Create a directory entry by adding an empty file with trailing slash - return $this->addFromString(rtrim($dirname, '/') . '/', ''); - } } diff --git a/vendor/phpoffice/phpword/src/PhpWord/SimpleType/Border.php b/vendor/phpoffice/phpword/src/PhpWord/SimpleType/Border.php index acd1c1a1..6cb42f04 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/SimpleType/Border.php +++ b/vendor/phpoffice/phpword/src/PhpWord/SimpleType/Border.php @@ -1,5 +1,4 @@ vAlign = null; - - return $this; - } - VerticalJc::validate($value); $this->vAlign = $this->setEnumVal($value, VerticalJc::values(), $this->vAlign); @@ -262,7 +235,7 @@ class Cell extends Border /** * Get vertical merge (rowspan). * - * @return null|string + * @return string */ public function getVMerge() { @@ -272,18 +245,12 @@ class Cell extends Border /** * Set vertical merge (rowspan). * - * @param null|string $value + * @param string $value * * @return self */ public function setVMerge($value = null) { - if ($value === null) { - $this->vMerge = null; - - return $this; - } - $enum = [self::VMERGE_RESTART, self::VMERGE_CONTINUE]; $this->vMerge = $this->setEnumVal($value, $enum, $this->vMerge); @@ -293,7 +260,7 @@ class Cell extends Border /** * Get shading. * - * @return Shading + * @return \PhpOffice\PhpWord\Style\Shading */ public function getShading() { @@ -377,84 +344,4 @@ class Cell extends Border { return $this->noWrap; } - - /** - * Get style padding-top. - */ - public function getPaddingTop(): ?int - { - return $this->paddingTop; - } - - /** - * Set style padding-top. - * - * @return $this - */ - public function setPaddingTop(int $value): self - { - $this->paddingTop = $value; - - return $this; - } - - /** - * Get style padding-bottom. - */ - public function getPaddingBottom(): ?int - { - return $this->paddingBottom; - } - - /** - * Set style padding-bottom. - * - * @return $this - */ - public function setPaddingBottom(int $value): self - { - $this->paddingBottom = $value; - - return $this; - } - - /** - * Get style padding-left. - */ - public function getPaddingLeft(): ?int - { - return $this->paddingLeft; - } - - /** - * Set style padding-left. - * - * @return $this - */ - public function setPaddingLeft(int $value): self - { - $this->paddingLeft = $value; - - return $this; - } - - /** - * Get style padding-right. - */ - public function getPaddingRight(): ?int - { - return $this->paddingRight; - } - - /** - * Set style padding-right. - * - * @return $this - */ - public function setPaddingRight(int $value): self - { - $this->paddingRight = $value; - - return $this; - } } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Style/Chart.php b/vendor/phpoffice/phpword/src/PhpWord/Style/Chart.php index 6cffc4d5..9dbc8db0 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Style/Chart.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Style/Chart.php @@ -1,5 +1,4 @@ color; } @@ -681,7 +682,7 @@ class Font extends AbstractStyle * * @param string $value * - * @return Table + * @return \PhpOffice\PhpWord\Style\Table */ public function setBgColor($value = null) { @@ -811,7 +812,7 @@ class Font extends AbstractStyle /** * Get paragraph style. * - * @return Paragraph + * @return \PhpOffice\PhpWord\Style\Paragraph */ public function getParagraph() { @@ -859,7 +860,7 @@ class Font extends AbstractStyle /** * Get shading. * - * @return Shading + * @return \PhpOffice\PhpWord\Style\Shading */ public function getShading() { @@ -883,7 +884,7 @@ class Font extends AbstractStyle /** * Get language. * - * @return null|Language + * @return null|\PhpOffice\PhpWord\Style\Language */ public function getLang() { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Style/Frame.php b/vendor/phpoffice/phpword/src/PhpWord/Style/Frame.php index 016722f3..45fc583e 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Style/Frame.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Style/Frame.php @@ -1,5 +1,4 @@ left; } /** * Set left. + * + * @param float|int $value + * + * @return self */ - public function setLeft(?float $value): self + public function setLeft($value) { - $this->left = $this->setNumericVal($value); + $this->left = $this->setNumericVal($value, $this->left); return $this; } /** * Get right. + * + * @return float|int */ - public function getRight(): ?float + public function getRight() { return $this->right; } /** * Set right. + * + * @param float|int $value + * + * @return self */ - public function setRight(?float $value): self + public function setRight($value) { - $this->right = $this->setNumericVal($value); + $this->right = $this->setNumericVal($value, $this->right); return $this; } /** * Get first line. + * + * @return float|int */ - public function getFirstLine(): ?float + public function getFirstLine() { return $this->firstLine; } /** * Set first line. + * + * @param float|int $value + * + * @return self */ - public function setFirstLine(?float $value): self + public function setFirstLine($value) { - $this->firstLine = $this->setNumericVal($value); - - return $this; - } - - /** - * Get first line chars. - */ - public function getFirstLineChars(): int - { - return $this->firstLineChars; - } - - /** - * Set first line chars. - */ - public function setFirstLineChars(int $value): self - { - $this->firstLineChars = $this->setIntVal($value, $this->firstLineChars); + $this->firstLine = $this->setNumericVal($value, $this->firstLine); return $this; } /** * Get hanging. + * + * @return float|int */ - public function getHanging(): ?float + public function getHanging() { return $this->hanging; } /** * Set hanging. + * + * @param float|int $value + * + * @return self */ - public function setHanging(?float $value = null): self + public function setHanging($value = null) { - $this->hanging = $this->setNumericVal($value); + $this->hanging = $this->setNumericVal($value, $this->hanging); return $this; } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Style/Language.php b/vendor/phpoffice/phpword/src/PhpWord/Style/Language.php index 54e43765..18e7f76e 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Style/Language.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Style/Language.php @@ -1,5 +1,4 @@ numId; } /** * Set Id. + * + * @param int $value + * + * @return self */ - public function setNumId(int $value): self + public function setNumId($value) { $this->numId = $this->setIntVal($value, $this->numId); @@ -73,16 +78,22 @@ class Numbering extends AbstractStyle /** * Get multilevel type. + * + * @return string */ - public function getType(): ?string + public function getType() { return $this->type; } /** * Set multilevel type. + * + * @param string $value + * + * @return self */ - public function setType(string $value): self + public function setType($value) { $enum = ['singleLevel', 'multilevel', 'hybridMultilevel']; $this->type = $this->setEnumVal($value, $enum, $this->type); @@ -95,15 +106,19 @@ class Numbering extends AbstractStyle * * @return NumberingLevel[] */ - public function getLevels(): array + public function getLevels() { return $this->levels; } /** * Set multilevel type. + * + * @param array $values + * + * @return self */ - public function setLevels(array $values): self + public function setLevels($values) { if (is_array($values)) { foreach ($values as $key => $value) { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Style/NumberingLevel.php b/vendor/phpoffice/phpword/src/PhpWord/Style/NumberingLevel.php index 31ec3738..39c0d839 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Style/NumberingLevel.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Style/NumberingLevel.php @@ -1,5 +1,4 @@ getChildStyleValue($this->indentation, 'hanging'); - } - /** * Get indentation. * - * @deprecated 1.4.0 Use getIndentLeft + * @return null|\PhpOffice\PhpWord\Style\Indentation */ - public function getIndent(): ?float - { - return $this->getChildStyleValue($this->indentation, 'left'); - } - - /** - * Get indentation. - */ - public function getIndentation(): ?Indentation + public function getIndentation() { return $this->indentation; } /** - * Get firstLine. - */ - public function getIndentFirstLine(): ?float - { - return $this->getChildStyleValue($this->indentation, 'firstLine'); - } - - /** - * Get left indentation. - */ - public function getIndentLeft(): ?float - { - return $this->getChildStyleValue($this->indentation, 'left'); - } - - /** - * Get right indentation. - */ - public function getIndentRight(): ?float - { - return $this->getChildStyleValue($this->indentation, 'right'); - } - - /** - * Set hanging. + * Set shading. * - * @deprecated 1.4.0 Use setIndentHanging - */ - public function setHanging(?float $value = null): self - { - return $this->setIndentation(['hanging' => $value]); - } - - /** - * Set indentation. + * @param mixed $value * - * @deprecated 1.4.0 Use setIndentLeft + * @return self */ - public function setIndent(?float $value = null): self + public function setIndentation($value = null) { - return $this->setIndentation(['left' => $value]); - } - - /** - * Set indentation. - * - * @param array{ - * left?:null|float|int|numeric-string, - * right?:null|float|int|numeric-string, - * hanging?:null|float|int|numeric-string, - * firstLine?:null|float|int|numeric-string - * } $value - */ - public function setIndentation(array $value = []): self - { - $value = array_map(function ($indent) { - if (is_string($indent) || is_numeric($indent)) { - $indent = $this->setFloatVal($indent); - } - - return $indent; - }, $value); $this->setObjectVal($value, 'Indentation', $this->indentation); return $this; } /** - * Set hanging indentation. + * Get indentation. + * + * @return int */ - public function setIndentHanging(?float $value = null): self + public function getIndent() { - return $this->setIndentation(['hanging' => $value]); + return $this->getChildStyleValue($this->indentation, 'left'); } /** - * Set firstline indentation. + * Set indentation. + * + * @param int $value + * + * @return self */ - public function setIndentFirstLine(?float $value = null): self - { - return $this->setIndentation(['firstLine' => $value]); - } - - /** - * Set firstlineChars indentation. - */ - public function setIndentFirstLineChars(int $value = 0): self - { - return $this->setIndentation(['firstLineChars' => $value]); - } - - /** - * Set left indentation. - */ - public function setIndentLeft(?float $value = null): self + public function setIndent($value = null) { return $this->setIndentation(['left' => $value]); } /** - * Set right indentation. + * Get hanging. + * + * @return int */ - public function setIndentRight(?float $value = null): self + public function getHanging() { - return $this->setIndentation(['right' => $value]); + return $this->getChildStyleValue($this->indentation, 'hanging'); + } + + /** + * Set hanging. + * + * @param int $value + * + * @return self + */ + public function setHanging($value = null) + { + return $this->setIndentation(['hanging' => $value]); } /** * Get spacing. * - * @return Spacing + * @return \PhpOffice\PhpWord\Style\Spacing * * @todo Rename to getSpacing in 1.0 */ @@ -565,7 +498,7 @@ class Paragraph extends Border * * @param string $value Possible values are defined in LineSpacingRule * - * @return Paragraph + * @return \PhpOffice\PhpWord\Style\Paragraph */ public function setSpacingLineRule($value) { @@ -753,7 +686,7 @@ class Paragraph extends Border /** * Get tabs. * - * @return Tab[] + * @return \PhpOffice\PhpWord\Style\Tab[] */ public function getTabs() { @@ -779,7 +712,7 @@ class Paragraph extends Border /** * Get shading. * - * @return Shading + * @return \PhpOffice\PhpWord\Style\Shading */ public function getShading() { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Style/Row.php b/vendor/phpoffice/phpword/src/PhpWord/Style/Row.php index 31ae3ded..765c54f8 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Style/Row.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Style/Row.php @@ -1,5 +1,4 @@ zip()->AddFromString("word/media/image1.jpg", file_get_contents($file));
* To read a file: $templateProcessor->zip()->getFromName("word/media/image1.jpg"); * - * @return ZipArchive + * @return \PhpOffice\PhpWord\Shared\ZipArchive */ public function zip() { @@ -269,7 +269,7 @@ class TemplateProcessor */ protected static function ensureUtf8Encoded($subject) { - return (null !== $subject) ? Text::toUTF8($subject) : ''; + return $subject ? Text::toUTF8($subject) : ''; } /** @@ -281,7 +281,7 @@ class TemplateProcessor $objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $elementName; $xmlWriter = new XMLWriter(); - /** @var Writer\Word2007\Element\AbstractElement $elementWriter */ + /** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $elementWriter */ $elementWriter = new $objectClass($xmlWriter, $complexType, true); $elementWriter->write(); @@ -308,7 +308,7 @@ class TemplateProcessor $objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $elementName; $xmlWriter = new XMLWriter(); - /** @var Writer\Word2007\Element\AbstractElement $elementWriter */ + /** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $elementWriter */ $elementWriter = new $objectClass($xmlWriter, $complexType, false); $elementWriter->write(); @@ -362,10 +362,10 @@ class TemplateProcessor /** * Set values from a one-dimensional array of "variable => value"-pairs. */ - public function setValues(array $values, int $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT): void + public function setValues(array $values): void { foreach ($values as $macro => $replace) { - $this->setValue($macro, $replace, $limit); + $this->setValue($macro, $replace); } } @@ -406,7 +406,7 @@ class TemplateProcessor $filename = "charts/chart{$rId}.xml"; // Get the part writer - $writerPart = new Writer\Word2007\Part\Chart(); + $writerPart = new \PhpOffice\PhpWord\Writer\Word2007\Part\Chart(); $writerPart->setElement($chart); // ContentTypes.xml @@ -499,22 +499,20 @@ class TemplateProcessor $widthFloat = $heightFloat * $imageRatio; $matches = []; preg_match('/\\d([a-z%]+)$/', $height, $matches); - $width = $widthFloat . (!empty($matches) ? $matches[1] : 'px'); + $width = $widthFloat . $matches[1]; } elseif ($height === '') { // defined height is empty $widthFloat = (float) $width; $heightFloat = $widthFloat / $imageRatio; $matches = []; preg_match('/\\d([a-z%]+)$/', $width, $matches); - $height = $heightFloat . (!empty($matches) ? $matches[1] : 'px'); + $height = $heightFloat . $matches[1]; } else { // we have defined size, but we need also check it aspect ratio $widthMatches = []; preg_match('/\\d([a-z%]+)$/', $width, $widthMatches); $heightMatches = []; preg_match('/\\d([a-z%]+)$/', $height, $heightMatches); // try to fix only if dimensions are same - if (!empty($widthMatches) - && !empty($heightMatches) - && $widthMatches[1] == $heightMatches[1]) { + if ($widthMatches[1] == $heightMatches[1]) { $dimention = $widthMatches[1]; $widthFloat = (float) $width; $heightFloat = (float) $height; @@ -1289,7 +1287,7 @@ class TemplateProcessor * @param int $count * @param string $xmlBlock * - * @return array + * @return string */ protected function indexClonedVariables($count, $xmlBlock) { @@ -1341,7 +1339,7 @@ class TemplateProcessor * @param string $block New block content * @param string $blockType XML tag type of block * - * @return TemplateProcessor Fluent interface + * @return \PhpOffice\PhpWord\TemplateProcessor Fluent interface */ public function replaceXmlBlock($macro, $block, $blockType = 'w:p') { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/AbstractWriter.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/AbstractWriter.php index 13859d82..8ebf98c7 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/AbstractWriter.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/AbstractWriter.php @@ -1,5 +1,4 @@ parts as $partName) { $partClass = 'PhpOffice\\PhpWord\\Writer\\HTML\\Part\\' . $partName; if (class_exists($partClass)) { - /** @var HTML\Part\AbstractPart $part Type hint */ + /** @var \PhpOffice\PhpWord\Writer\HTML\Part\AbstractPart $part Type hint */ $part = new $partClass(); $part->setParentWriter($this); $this->writerParts[strtolower($partName)] = $part; diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/AbstractElement.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/AbstractElement.php index 0e6a112e..7c7bde31 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/AbstractElement.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/AbstractElement.php @@ -1,5 +1,4 @@ namespace, $elementClass); if (class_exists($writerClass)) { - /** @var AbstractElement $writer Type hint */ + /** @var \PhpOffice\PhpWord\Writer\HTML\Element\AbstractElement $writer Type hint */ $writer = new $writerClass($this->parentWriter, $element, $withoutP); $content .= $writer->write(); } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/Endnote.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/Endnote.php index 7e7f31d4..1c35e8fa 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/Endnote.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/Endnote.php @@ -1,5 +1,4 @@ element; - $text = $this->parentWriter->escapeHTML($element->getText() ?? ''); + $text = $this->parentWriter->escapeHTML($element->getText()); if (!$this->withoutP && !trim($text)) { $text = ' '; } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/TextBreak.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/TextBreak.php index 576f6a83..af73cb4a 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/TextBreak.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Element/TextBreak.php @@ -1,5 +1,4 @@ ' . PHP_EOL; - $defaultFontColor = Settings::getDefaultFontColor(); + // Default styles $astarray = [ 'font-family' => $this->getFontFamily(Settings::getDefaultFontName(), $this->getParentWriter()->getDefaultGenericFont()), 'font-size' => Settings::getDefaultFontSize() . 'pt', - 'color' => "#{$defaultFontColor}", ]; // Mpdf sometimes needs separate tag for body; doesn't harm others. $bodyarray = $astarray; diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Style/AbstractStyle.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Style/AbstractStyle.php index 4672347b..a6507867 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Style/AbstractStyle.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/HTML/Style/AbstractStyle.php @@ -1,5 +1,4 @@ getVAlign(); - } foreach (['Top', 'Left', 'Bottom', 'Right'] as $direction) { $method = 'getBorder' . $direction . 'Style'; diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText.php index c9a524e8..616119e5 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText.php @@ -1,5 +1,4 @@ parts) as $partName) { $partClass = static::class . '\\Part\\' . $partName; if (class_exists($partClass)) { - /** @var AbstractPart $partObject Type hint */ + /** @var \PhpOffice\PhpWord\Writer\ODText\Part\AbstractPart $partObject Type hint */ $partObject = new $partClass(); $partObject->setParentWriter($this); $this->writerParts[strtolower($partName)] = $partObject; diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/AbstractElement.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/AbstractElement.php index 97d1875c..5cd396aa 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/AbstractElement.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/AbstractElement.php @@ -1,5 +1,4 @@ startElement('text:s'); - $xmlWriter->writeAttributeIf($num > 1, 'text:c', "$num"); - $xmlWriter->endElement(); - $text = preg_replace('/^ +/', '', $text); - } - preg_match_all('/([\\s\\S]*?)(\\t| +| ?$)/', $text, $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - $this->writeText($match[1]); - if ($match[2] === '') { - break; - } elseif ($match[2] === "\t") { - $xmlWriter->writeElement('text:tab'); - } elseif ($match[2] === ' ') { - $xmlWriter->writeElement('text:s'); - - break; - } else { - $num = strlen($match[2]); - $xmlWriter->startElement('text:s'); - $xmlWriter->writeAttributeIf($num > 1, 'text:c', "$num"); - $xmlWriter->endElement(); - } - } - } } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Container.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Container.php index 6b0fee8c..6e6b88ea 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Container.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Container.php @@ -1,5 +1,4 @@ - */ - protected $containerWithoutP = ['TextRun', 'Footnote', 'Endnote']; } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Field.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Field.php index 6b548078..46f62b0f 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Field.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Field.php @@ -1,5 +1,4 @@ getXmlWriter(); $element = $this->getElement(); - if (!$element instanceof ElementImage) { + if (!$element instanceof \PhpOffice\PhpWord\Element\Image) { return; } @@ -44,16 +43,11 @@ class Image extends AbstractElement $width = Converter::pixelToCm($style->getWidth()); $height = Converter::pixelToCm($style->getHeight()); - $xmlWriter = $this->getXmlWriter(); - - if (!$this->withoutP) { - $xmlWriter->startElement('text:p'); - $xmlWriter->writeAttribute('text:style-name', 'IM' . $mediaIndex); - } + $xmlWriter->startElement('text:p'); + $xmlWriter->writeAttribute('text:style-name', 'IM' . $mediaIndex); $xmlWriter->startElement('draw:frame'); $xmlWriter->writeAttribute('draw:style-name', 'fr' . $mediaIndex); - $xmlWriter->writeAttributeIf($this->withoutP, 'draw:text-style-name', 'IM' . $mediaIndex); $xmlWriter->writeAttribute('draw:name', $element->getElementId()); $xmlWriter->writeAttribute('text:anchor-type', 'as-char'); $xmlWriter->writeAttribute('svg:width', $width . 'cm'); @@ -69,8 +63,6 @@ class Image extends AbstractElement $xmlWriter->endElement(); // draw:frame - if (!$this->withoutP) { - $xmlWriter->endElement(); // text:p - } + $xmlWriter->endElement(); // text:p } } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Link.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Link.php index 9ef35692..0375b11b 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Link.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Link.php @@ -1,5 +1,4 @@ getXmlWriter(); $element = $this->getElement(); - if (!$element instanceof TableElement) { + if (!$element instanceof \PhpOffice\PhpWord\Element\Table) { return; } $rows = $element->getRows(); @@ -78,7 +77,7 @@ class Table extends AbstractElement private function writeRow(XMLWriter $xmlWriter, RowElement $row): void { $xmlWriter->startElement('table:table-row'); - /** @var RowElement $row Type hint */ + /** @var \PhpOffice\PhpWord\Element\Row $row Type hint */ foreach ($row->getCells() as $cell) { $xmlWriter->startElement('table:table-cell'); $xmlWriter->writeAttribute('office:value-type', 'string'); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Text.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Text.php index 39969723..75fb9308 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Text.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/Text.php @@ -1,5 +1,4 @@ writeAttribute('text:change-id', $element->getTrackChange()->getElementId()); $xmlWriter->endElement(); } else { - if (empty($paragraphStyle)) { - if (!$this->withoutP) { - $xmlWriter->writeAttribute('text:style-name', 'Normal'); + if (empty($fontStyle)) { + if (empty($paragraphStyle)) { + if (!$this->withoutP) { + $xmlWriter->writeAttribute('text:style-name', 'Normal'); + } + } elseif (is_string($paragraphStyle)) { + if (!$this->withoutP) { + $xmlWriter->writeAttribute('text:style-name', $paragraphStyle); + } } - } elseif (is_string($paragraphStyle)) { - if (!$this->withoutP) { - $xmlWriter->writeAttribute('text:style-name', $paragraphStyle); + $this->writeChangeInsertion(true, $element->getTrackChange()); + $this->replaceTabs($element->getText(), $xmlWriter); + $this->writeChangeInsertion(false, $element->getTrackChange()); + } else { + if (empty($paragraphStyle)) { + if (!$this->withoutP) { + $xmlWriter->writeAttribute('text:style-name', 'Normal'); + } + } elseif (is_string($paragraphStyle)) { + if (!$this->withoutP) { + $xmlWriter->writeAttribute('text:style-name', $paragraphStyle); + } } - } - - if (!empty($fontStyle)) { // text:span $xmlWriter->startElement('text:span'); if (is_string($fontStyle)) { $xmlWriter->writeAttribute('text:style-name', $fontStyle); } - } - - $this->writeChangeInsertion(true, $element->getTrackChange()); - $this->replaceTabs($element->getText(), $xmlWriter); - $this->writeChangeInsertion(false, $element->getTrackChange()); - - if (!empty($fontStyle)) { + $this->writeChangeInsertion(true, $element->getTrackChange()); + $this->replaceTabs($element->getText(), $xmlWriter); + $this->writeChangeInsertion(false, $element->getTrackChange()); $xmlWriter->endElement(); } } @@ -89,6 +96,35 @@ class Text extends AbstractElement } } + private function replacetabs($text, $xmlWriter): void + { + if (preg_match('/^ +/', $text, $matches)) { + $num = strlen($matches[0]); + $xmlWriter->startElement('text:s'); + $xmlWriter->writeAttributeIf($num > 1, 'text:c', "$num"); + $xmlWriter->endElement(); + $text = preg_replace('/^ +/', '', $text); + } + preg_match_all('/([\\s\\S]*?)(\\t| +| ?$)/', $text, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $this->writeText($match[1]); + if ($match[2] === '') { + break; + } elseif ($match[2] === "\t") { + $xmlWriter->writeElement('text:tab'); + } elseif ($match[2] === ' ') { + $xmlWriter->writeElement('text:s'); + + break; + } else { + $num = strlen($match[2]); + $xmlWriter->startElement('text:s'); + $xmlWriter->writeAttributeIf($num > 1, 'text:c', "$num"); + $xmlWriter->endElement(); + } + } + } + private function writeChangeInsertion($start = true, ?TrackChange $trackChange = null): void { if ($trackChange == null || $trackChange->getChangeType() != TrackChange::INSERTED) { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/TextBreak.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/TextBreak.php index 1a697007..1bfe3988 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/TextBreak.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Element/TextBreak.php @@ -1,5 +1,4 @@ imageParagraphStyles as $style) { - $styleWriter = new ParagraphStyleWriter($xmlWriter, $style); + $styleWriter = new \PhpOffice\PhpWord\Writer\ODText\Style\Paragraph($xmlWriter, $style); $styleWriter->write(); } } @@ -257,7 +256,7 @@ class Content extends AbstractPart * * Table style can be null or string of the style name * - * @param AbstractContainer $container + * @param \PhpOffice\PhpWord\Element\AbstractContainer $container * @param int $paragraphStyleCount * @param int $fontStyleCount * @@ -278,7 +277,7 @@ class Content extends AbstractPart $style = $element->getStyle(); $style->setStyleName('fr' . $element->getMediaIndex()); $this->autoStyles['Image'][] = $style; - $sty = new Paragraph(); + $sty = new \PhpOffice\PhpWord\Style\Paragraph(); $sty->setStyleName('IM' . $element->getMediaIndex()); $sty->setAuto(); $sty->setAlignment($style->getAlignment()); @@ -301,7 +300,7 @@ class Content extends AbstractPart /** * Get style of individual element. * - * @param Text $element + * @param \PhpOffice\PhpWord\Element\Text $element * @param int $paragraphStyleCount * @param int $fontStyleCount */ @@ -347,7 +346,7 @@ class Content extends AbstractPart /** * Get font style of individual field element. * - * @param Field $element + * @param \PhpOffice\PhpWord\Element\Field $element * @param int $fontStyleCount */ private function getElementStyleField($element, &$fontStyleCount): void @@ -372,7 +371,7 @@ class Content extends AbstractPart /** * Get style of individual element. * - * @param TextRun $element + * @param \PhpOffice\PhpWord\Element\TextRun $element * @param int $paragraphStyleCount */ private function getElementStyleTextRun($element, &$paragraphStyleCount): void diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Part/Manifest.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Part/Manifest.php index 200da158..37fb7979 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Part/Manifest.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Part/Manifest.php @@ -1,5 +1,4 @@ startElement('style:text-properties'); - $xmlWriter->writeAttribute('style:use-window-font-color', 'false'); + $xmlWriter->writeAttribute('style:use-window-font-color', 'true'); $xmlWriter->writeAttribute('style:font-name', Settings::getDefaultFontName()); $xmlWriter->writeAttribute('fo:font-size', Settings::getDefaultFontSize() . 'pt'); $xmlWriter->writeAttribute('fo:language', $latinLang[0]); $xmlWriter->writeAttribute('fo:country', $latinLang[1]); - $xmlWriter->writeAttribute('fo:color', '#' . Settings::getDefaultFontColor()); $xmlWriter->writeAttribute('style:letter-kerning', 'true'); $xmlWriter->writeAttribute('style:font-name-asian', Settings::getDefaultFontName() . '2'); $xmlWriter->writeAttribute('style:font-size-asian', Settings::getDefaultFontSize() . 'pt'); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/AbstractStyle.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/AbstractStyle.php index 3545009f..439434c9 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/AbstractStyle.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/AbstractStyle.php @@ -1,5 +1,4 @@ setStyleName($style->getStyleName()); - $temp2 = new Paragraph($xmlWriter, $temp1); + $temp2 = new \PhpOffice\PhpWord\Writer\ODText\Style\Paragraph($xmlWriter, $temp1); $temp2->write(); } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/Image.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/Image.php index 56c4f57a..79ddfc50 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/Image.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/Image.php @@ -1,5 +1,4 @@ getStyle(); - if (!$style instanceof Style\Paragraph) { + if (!$style instanceof \PhpOffice\PhpWord\Style\Paragraph) { return; } $xmlWriter = $this->getXmlWriter(); @@ -74,13 +73,13 @@ class Paragraph extends AbstractStyle } elseif (substr($styleName, 0, 2) === 'HD') { $styleAuto = true; $psm = 'Heading_' . substr($styleName, 2); - $stylep = Style::getStyle($psm); - if ($stylep instanceof Style\Font) { + $stylep = \PhpOffice\PhpWord\Style::getStyle($psm); + if ($stylep instanceof \PhpOffice\PhpWord\Style\Font) { if (method_exists($stylep, 'getParagraph')) { $stylep = $stylep->getParagraph(); } } - if ($stylep instanceof Style\Paragraph) { + if ($stylep instanceof \PhpOffice\PhpWord\Style\Paragraph) { if ($stylep->hasPageBreakBefore()) { $breakbefore = true; } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/Section.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/Section.php index 05152a39..0a250194 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/Section.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/ODText/Style/Section.php @@ -1,5 +1,4 @@ parts as $partName) { $partClass = static::class . '\\Part\\' . $partName; if (class_exists($partClass)) { - /** @var RTF\Part\AbstractPart $part Type hint */ + /** @var \PhpOffice\PhpWord\Writer\RTF\Part\AbstractPart $part Type hint */ $part = new $partClass(); $part->setParentWriter($this); $this->writerParts[strtolower($partName)] = $part; diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Element/AbstractElement.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Element/AbstractElement.php index e007e6aa..5c33868a 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Element/AbstractElement.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Element/AbstractElement.php @@ -1,5 +1,4 @@ parentWriter; /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ @@ -189,7 +188,7 @@ abstract class AbstractElement return ''; } - /** @var WriterRTF $parentWriter Type hint */ + /** @var \PhpOffice\PhpWord\Writer\RTF $parentWriter Type hint */ $parentWriter = $this->parentWriter; // Create style writer and set color/name index diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Element/Container.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Element/Container.php index dcac8ec0..5e198aec 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Element/Container.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Element/Container.php @@ -1,5 +1,4 @@ element; $elementClass = str_replace('\\Writer\\RTF', '', static::class); - if (!$element instanceof $elementClass) { + if (!$element instanceof $elementClass || !is_string($element->getText())) { return ''; } - $textToWrite = $element->getText(); - if ($textToWrite instanceof \PhpOffice\PhpWord\Element\TextRun) { - $textToWrite = $textToWrite->getText(); // gets text from TextRun - } - $this->getStyles(); $content = ''; @@ -88,7 +82,7 @@ class Title extends Text $content .= '{'; $content .= $this->writeFontStyle(); - $content .= $this->writeText($textToWrite); + $content .= $this->writeText($element->getText()); $content .= '}'; $content .= $this->writeClosing(); $content .= $endout; diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Part/AbstractPart.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Part/AbstractPart.php index a07f70bb..be772b93 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Part/AbstractPart.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Part/AbstractPart.php @@ -1,5 +1,4 @@ getHeaders() as $header) { $type = $header->getType(); - if ($evenOdd || $type !== Footer::EVEN) { + if ($evenOdd || $type !== FOOTER::EVEN) { $content .= '{\\header'; if ($type === Footer::FIRST) { $content .= 'f'; } elseif ($evenOdd) { - $content .= ($type === Footer::EVEN) ? 'l' : 'r'; + $content .= ($type === FOOTER::EVEN) ? 'l' : 'r'; } foreach ($header->getElements() as $element) { $cl = get_class($element); @@ -183,12 +182,12 @@ class Document extends AbstractPart } foreach ($section->getFooters() as $footer) { $type = $footer->getType(); - if ($evenOdd || $type !== Footer::EVEN) { + if ($evenOdd || $type !== FOOTER::EVEN) { $content .= '{\\footer'; if ($type === Footer::FIRST) { $content .= 'f'; } elseif ($evenOdd) { - $content .= ($type === Footer::EVEN) ? 'l' : 'r'; + $content .= ($type === FOOTER::EVEN) ? 'l' : 'r'; } foreach ($footer->getElements() as $element) { $cl = get_class($element); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Part/Header.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Part/Header.php index 97644fe4..7f8cc84b 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Part/Header.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/RTF/Part/Header.php @@ -1,5 +1,4 @@ parts) as $partName) { $partClass = static::class . '\\Part\\' . $partName; if (class_exists($partClass)) { - /** @var Word2007\Part\AbstractPart $part Type hint */ + /** @var \PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart $part Type hint */ $part = new $partClass(); $part->setParentWriter($this); $this->writerParts[strtolower($partName)] = $part; @@ -178,7 +179,7 @@ class Word2007 extends AbstractWriter implements WriterInterface $this->registerContentTypes($media); } - /** @var Word2007\Part\AbstractPart $writerPart Type hint */ + /** @var \PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart $writerPart Type hint */ $writerPart = $this->getWriterPart('relspart')->setMedia($media); $zip->addFromString("word/_rels/{$file}.xml.rels", $writerPart->write()); } @@ -205,7 +206,7 @@ class Word2007 extends AbstractWriter implements WriterInterface $this->contentTypes['override']["/word/$elmFile"] = $elmType; $this->relationships[] = ['target' => $elmFile, 'type' => $elmType, 'rID' => $rId]; - /** @var Word2007\Part\AbstractPart $writerPart Type hint */ + /** @var \PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart $writerPart Type hint */ $writerPart = $this->getWriterPart($elmType)->setElement($element); $zip->addFromString("word/$elmFile", $writerPart->write()); } @@ -235,7 +236,7 @@ class Word2007 extends AbstractWriter implements WriterInterface // Write relationships file, e.g. word/_rels/footnotes.xml if (!empty($media)) { - /** @var Word2007\Part\AbstractPart $writerPart Type hint */ + /** @var \PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart $writerPart Type hint */ $writerPart = $this->getWriterPart('relspart')->setMedia($media); $zip->addFromString("word/_rels/{$partName}.xml.rels", $writerPart->write()); } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/AbstractElement.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/AbstractElement.php index 5743c8c7..b677556d 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/AbstractElement.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/AbstractElement.php @@ -1,5 +1,4 @@ xmlWriter = $xmlWriter; $this->element = $element; @@ -75,7 +76,7 @@ abstract class AbstractElement /** * Get XML Writer. * - * @return XMLWriter + * @return \PhpOffice\PhpWord\Shared\XMLWriter */ protected function getXmlWriter() { @@ -85,7 +86,7 @@ abstract class AbstractElement /** * Get element. * - * @return Element + * @return \PhpOffice\PhpWord\Element\AbstractElement */ protected function getElement() { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Bookmark.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Bookmark.php index ba61ad69..1e618af9 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Bookmark.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Bookmark.php @@ -1,5 +1,4 @@ - */ - protected $containerWithoutP = ['TextRun', 'Footnote', 'Endnote', 'ListItemRun']; - /** * Write element. */ @@ -52,7 +46,7 @@ class Container extends AbstractElement return; } $containerClass = substr(get_class($container), strrpos(get_class($container), '\\') + 1); - $withoutP = in_array($containerClass, $this->containerWithoutP); + $withoutP = in_array($containerClass, ['TextRun', 'Footnote', 'Endnote', 'ListItemRun']); $xmlWriter = $this->getXmlWriter(); // Loop through elements @@ -68,7 +62,7 @@ class Container extends AbstractElement $writeLastTextBreak = ($containerClass == 'Cell') && ($elementClass == '' || $elementClass == 'Table'); if ($writeLastTextBreak) { $writerClass = $this->namespace . '\\TextBreak'; - /** @var AbstractElement $writer Type hint */ + /** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $writer Type hint */ $writer = new $writerClass($xmlWriter, new TextBreakElement(), $withoutP); $writer->write(); } @@ -76,14 +70,18 @@ class Container extends AbstractElement /** * Write individual element. + * + * @param bool $withoutP + * + * @return string */ - private function writeElement(XMLWriter $xmlWriter, Element $element, bool $withoutP): string + private function writeElement(XMLWriter $xmlWriter, Element $element, $withoutP) { $elementClass = substr(get_class($element), strrpos(get_class($element), '\\') + 1); $writerClass = $this->namespace . '\\' . $elementClass; if (class_exists($writerClass)) { - /** @var AbstractElement $writer Type hint */ + /** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $writer Type hint */ $writer = new $writerClass($xmlWriter, $element, $withoutP); $writer->setPart($this->getPart()); $writer->write(); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Endnote.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Endnote.php index 6a00ed5b..f96ac797 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Endnote.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Endnote.php @@ -1,5 +1,4 @@ endElement(); // w:r if ($element->getText() != null) { - if ($element->getText() instanceof TextRun) { + if ($element->getText() instanceof \PhpOffice\PhpWord\Element\TextRun) { $containerWriter = new Container($xmlWriter, $element->getText(), true); $containerWriter->write(); } @@ -263,7 +262,7 @@ class Field extends Text $xmlWriter->endElement(); // w:r if ($element->getText() != null) { - if ($element->getText() instanceof TextRun) { + if ($element->getText() instanceof \PhpOffice\PhpWord\Element\TextRun) { $containerWriter = new Container($xmlWriter, $element->getText(), true); $containerWriter->write(); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Footnote.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Footnote.php index 68f998e3..77073a23 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Footnote.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Footnote.php @@ -1,5 +1,4 @@ startElement('w:r'); $xmlWriter->startElement('w:instrText'); $xmlWriter->writeAttribute('xml:space', 'preserve'); - $xmlWriter->text("PAGEREF $rId \\h"); + $xmlWriter->text("PAGEREF _Toc{$rId} \\h"); $xmlWriter->endElement(); $xmlWriter->endElement(); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Table.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Table.php index 2bb1b3f3..a32cc196 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Table.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/Table.php @@ -1,5 +1,4 @@ getBgColor()) { $xmlWriter->writeAttribute('fillcolor', $style->getBgColor()); - } else { - $xmlWriter->writeAttribute('filled', 'f'); - } - - if (!$style->getBorderColor()) { - $xmlWriter->writeAttribute('stroked', 'f'); - $xmlWriter->writeAttribute('strokecolor', 'white'); } $styleWriter->write(); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/TextBreak.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/TextBreak.php index 4c2ecde7..bcae3b2e 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/TextBreak.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Element/TextBreak.php @@ -1,5 +1,4 @@ getSalt() == null) { - $documentProtection->setSalt((string) openssl_random_pseudo_bytes(16)); + $documentProtection->setSalt(openssl_random_pseudo_bytes(16)); } $passwordHash = PasswordEncoder::hashPassword($documentProtection->getPassword(), $documentProtection->getAlgorithm(), $documentProtection->getSalt(), $documentProtection->getSpinCount()); $this->settings['w:documentProtection'] = [ diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Part/Styles.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Part/Styles.php index edf0314c..2112fd3c 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Part/Styles.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Part/Styles.php @@ -1,5 +1,4 @@ getParentWriter()->getPhpWord(); $fontName = $phpWord->getDefaultFontName(); - $asianFontName = $phpWord->getDefaultAsianFontName(); $fontSize = $phpWord->getDefaultFontSize(); - $fontColor = $phpWord->getDefaultFontColor(); $language = $phpWord->getSettings()->getThemeFontLang(); $latinLanguage = ($language == null || $language->getLatin() === null) ? 'en-US' : $language->getLatin(); @@ -97,12 +94,9 @@ class Styles extends AbstractPart $xmlWriter->startElement('w:rFonts'); $xmlWriter->writeAttribute('w:ascii', $fontName); $xmlWriter->writeAttribute('w:hAnsi', $fontName); - $xmlWriter->writeAttribute('w:eastAsia', $asianFontName); + $xmlWriter->writeAttribute('w:eastAsia', $fontName); $xmlWriter->writeAttribute('w:cs', $fontName); $xmlWriter->endElement(); // w:rFonts - $xmlWriter->startElement('w:color'); - $xmlWriter->writeAttribute('w:val', $fontColor); - $xmlWriter->endElement(); $xmlWriter->startElement('w:sz'); $xmlWriter->writeAttribute('w:val', $fontSize * 2); $xmlWriter->endElement(); // w:sz @@ -131,7 +125,7 @@ class Styles extends AbstractPart if (isset($styles['Normal'])) { $normalStyle = $styles['Normal']; // w:pPr - if ($normalStyle instanceof FontStyle && $normalStyle->getParagraph() != null) { + if ($normalStyle instanceof Fontstyle && $normalStyle->getParagraph() != null) { $styleWriter = new ParagraphStyleWriter($xmlWriter, $normalStyle->getParagraph()); $styleWriter->write(); } elseif ($normalStyle instanceof ParagraphStyle) { diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Part/Theme.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Part/Theme.php index a70c248d..ad57d664 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Part/Theme.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Part/Theme.php @@ -1,5 +1,4 @@ write(); } diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Cell.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Cell.php index bb0d6d71..6e22597d 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Cell.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Cell.php @@ -1,5 +1,4 @@ endElement(); // w:tcW } - $paddingTop = $style->getPaddingTop(); - $paddingLeft = $style->getPaddingLeft(); - $paddingBottom = $style->getPaddingBottom(); - $paddingRight = $style->getPaddingRight(); - - if ($paddingTop !== null || $paddingLeft !== null || $paddingBottom !== null || $paddingRight !== null) { - $xmlWriter->startElement('w:tcMar'); - if ($paddingTop !== null) { - $xmlWriter->startElement('w:top'); - $xmlWriter->writeAttribute('w:w', $paddingTop); - $xmlWriter->writeAttribute('w:type', \PhpOffice\PhpWord\SimpleType\TblWidth::TWIP); - $xmlWriter->endElement(); // w:top - } - if ($paddingLeft !== null) { - $xmlWriter->startElement('w:start'); - $xmlWriter->writeAttribute('w:w', $paddingLeft); - $xmlWriter->writeAttribute('w:type', \PhpOffice\PhpWord\SimpleType\TblWidth::TWIP); - $xmlWriter->endElement(); // w:start - } - if ($paddingBottom !== null) { - $xmlWriter->startElement('w:bottom'); - $xmlWriter->writeAttribute('w:w', $paddingBottom); - $xmlWriter->writeAttribute('w:type', \PhpOffice\PhpWord\SimpleType\TblWidth::TWIP); - $xmlWriter->endElement(); // w:bottom - } - if ($paddingRight !== null) { - $xmlWriter->startElement('w:end'); - $xmlWriter->writeAttribute('w:w', $paddingRight); - $xmlWriter->writeAttribute('w:type', \PhpOffice\PhpWord\SimpleType\TblWidth::TWIP); - $xmlWriter->endElement(); // w:end - } - $xmlWriter->endElement(); // w:tcMar - } - // Text direction $textDir = $style->getTextDirection(); $xmlWriter->writeElementIf(null !== $textDir, 'w:textDirection', 'w:val', $textDir); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Extrusion.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Extrusion.php index 8bb92187..f6ad6221 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Extrusion.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Extrusion.php @@ -1,5 +1,4 @@ getFirstLine(); $xmlWriter->writeAttributeIf(null !== $firstLine, 'w:firstLine', $this->convertTwip($firstLine)); - $firstLineChars = $style->getFirstLineChars(); - $xmlWriter->writeAttributeIf(0 !== $firstLineChars, 'w:firstLineChars', $this->convertTwip($firstLineChars)); - $hanging = $style->getHanging(); $xmlWriter->writeAttributeIf(null !== $hanging, 'w:hanging', $this->convertTwip($hanging)); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Line.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Line.php index 2603545f..90107f8e 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Line.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Line.php @@ -1,5 +1,4 @@ startElement('w:numPr'); diff --git a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Row.php b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Row.php index 2b9d804f..7e1468e8 100644 --- a/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Row.php +++ b/vendor/phpoffice/phpword/src/PhpWord/Writer/Word2007/Style/Row.php @@ -1,5 +1,4 @@ log(LogLevel::EMERGENCY, $message, $context); - } - - /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function alert($message, array $context = array()) - { - $this->log(LogLevel::ALERT, $message, $context); - } - - /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function critical($message, array $context = array()) - { - $this->log(LogLevel::CRITICAL, $message, $context); - } - - /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function error($message, array $context = array()) - { - $this->log(LogLevel::ERROR, $message, $context); - } - - /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function warning($message, array $context = array()) - { - $this->log(LogLevel::WARNING, $message, $context); - } - - /** - * Normal but significant events. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function notice($message, array $context = array()) - { - $this->log(LogLevel::NOTICE, $message, $context); - } - - /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function info($message, array $context = array()) - { - $this->log(LogLevel::INFO, $message, $context); - } - - /** - * Detailed debug information. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function debug($message, array $context = array()) - { - $this->log(LogLevel::DEBUG, $message, $context); - } -} diff --git a/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/vendor/psr/log/Psr/Log/InvalidArgumentException.php deleted file mode 100644 index 67f852d1..00000000 --- a/vendor/psr/log/Psr/Log/InvalidArgumentException.php +++ /dev/null @@ -1,7 +0,0 @@ -logger = $logger; - } -} diff --git a/vendor/psr/log/Psr/Log/LoggerInterface.php b/vendor/psr/log/Psr/Log/LoggerInterface.php deleted file mode 100644 index 2206cfde..00000000 --- a/vendor/psr/log/Psr/Log/LoggerInterface.php +++ /dev/null @@ -1,125 +0,0 @@ -log(LogLevel::EMERGENCY, $message, $context); - } - - /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function alert($message, array $context = array()) - { - $this->log(LogLevel::ALERT, $message, $context); - } - - /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function critical($message, array $context = array()) - { - $this->log(LogLevel::CRITICAL, $message, $context); - } - - /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function error($message, array $context = array()) - { - $this->log(LogLevel::ERROR, $message, $context); - } - - /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function warning($message, array $context = array()) - { - $this->log(LogLevel::WARNING, $message, $context); - } - - /** - * Normal but significant events. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function notice($message, array $context = array()) - { - $this->log(LogLevel::NOTICE, $message, $context); - } - - /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function info($message, array $context = array()) - { - $this->log(LogLevel::INFO, $message, $context); - } - - /** - * Detailed debug information. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function debug($message, array $context = array()) - { - $this->log(LogLevel::DEBUG, $message, $context); - } - - /** - * Logs with an arbitrary level. - * - * @param mixed $level - * @param string $message - * @param array $context - * - * @return void - * - * @throws \Psr\Log\InvalidArgumentException - */ - abstract public function log($level, $message, array $context = array()); -} diff --git a/vendor/psr/log/Psr/Log/NullLogger.php b/vendor/psr/log/Psr/Log/NullLogger.php deleted file mode 100644 index c8f7293b..00000000 --- a/vendor/psr/log/Psr/Log/NullLogger.php +++ /dev/null @@ -1,30 +0,0 @@ -logger) { }` - * blocks. - */ -class NullLogger extends AbstractLogger -{ - /** - * Logs with an arbitrary level. - * - * @param mixed $level - * @param string $message - * @param array $context - * - * @return void - * - * @throws \Psr\Log\InvalidArgumentException - */ - public function log($level, $message, array $context = array()) - { - // noop - } -} diff --git a/vendor/psr/log/Psr/Log/Test/DummyTest.php b/vendor/psr/log/Psr/Log/Test/DummyTest.php deleted file mode 100644 index 9638c110..00000000 --- a/vendor/psr/log/Psr/Log/Test/DummyTest.php +++ /dev/null @@ -1,18 +0,0 @@ - ". - * - * Example ->error('Foo') would yield "error Foo". - * - * @return string[] - */ - abstract public function getLogs(); - - public function testImplements() - { - $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); - } - - /** - * @dataProvider provideLevelsAndMessages - */ - public function testLogsAtAllLevels($level, $message) - { - $logger = $this->getLogger(); - $logger->{$level}($message, array('user' => 'Bob')); - $logger->log($level, $message, array('user' => 'Bob')); - - $expected = array( - $level.' message of level '.$level.' with context: Bob', - $level.' message of level '.$level.' with context: Bob', - ); - $this->assertEquals($expected, $this->getLogs()); - } - - public function provideLevelsAndMessages() - { - return array( - LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), - LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), - LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), - LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), - LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), - LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), - LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), - LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), - ); - } - - /** - * @expectedException \Psr\Log\InvalidArgumentException - */ - public function testThrowsOnInvalidLevel() - { - $logger = $this->getLogger(); - $logger->log('invalid level', 'Foo'); - } - - public function testContextReplacement() - { - $logger = $this->getLogger(); - $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); - - $expected = array('info {Message {nothing} Bob Bar a}'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testObjectCastToString() - { - if (method_exists($this, 'createPartialMock')) { - $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString')); - } else { - $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); - } - $dummy->expects($this->once()) - ->method('__toString') - ->will($this->returnValue('DUMMY')); - - $this->getLogger()->warning($dummy); - - $expected = array('warning DUMMY'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testContextCanContainAnything() - { - $closed = fopen('php://memory', 'r'); - fclose($closed); - - $context = array( - 'bool' => true, - 'null' => null, - 'string' => 'Foo', - 'int' => 0, - 'float' => 0.5, - 'nested' => array('with object' => new DummyTest), - 'object' => new \DateTime, - 'resource' => fopen('php://memory', 'r'), - 'closed' => $closed, - ); - - $this->getLogger()->warning('Crazy context data', $context); - - $expected = array('warning Crazy context data'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testContextExceptionKeyCanBeExceptionOrOtherValues() - { - $logger = $this->getLogger(); - $logger->warning('Random message', array('exception' => 'oops')); - $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); - - $expected = array( - 'warning Random message', - 'critical Uncaught Exception!' - ); - $this->assertEquals($expected, $this->getLogs()); - } -} diff --git a/vendor/psr/log/Psr/Log/Test/TestLogger.php b/vendor/psr/log/Psr/Log/Test/TestLogger.php deleted file mode 100644 index 1be32304..00000000 --- a/vendor/psr/log/Psr/Log/Test/TestLogger.php +++ /dev/null @@ -1,147 +0,0 @@ - $level, - 'message' => $message, - 'context' => $context, - ]; - - $this->recordsByLevel[$record['level']][] = $record; - $this->records[] = $record; - } - - public function hasRecords($level) - { - return isset($this->recordsByLevel[$level]); - } - - public function hasRecord($record, $level) - { - if (is_string($record)) { - $record = ['message' => $record]; - } - return $this->hasRecordThatPasses(function ($rec) use ($record) { - if ($rec['message'] !== $record['message']) { - return false; - } - if (isset($record['context']) && $rec['context'] !== $record['context']) { - return false; - } - return true; - }, $level); - } - - public function hasRecordThatContains($message, $level) - { - return $this->hasRecordThatPasses(function ($rec) use ($message) { - return strpos($rec['message'], $message) !== false; - }, $level); - } - - public function hasRecordThatMatches($regex, $level) - { - return $this->hasRecordThatPasses(function ($rec) use ($regex) { - return preg_match($regex, $rec['message']) > 0; - }, $level); - } - - public function hasRecordThatPasses(callable $predicate, $level) - { - if (!isset($this->recordsByLevel[$level])) { - return false; - } - foreach ($this->recordsByLevel[$level] as $i => $rec) { - if (call_user_func($predicate, $rec, $i)) { - return true; - } - } - return false; - } - - public function __call($method, $args) - { - if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { - $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; - $level = strtolower($matches[2]); - if (method_exists($this, $genericMethod)) { - $args[] = $level; - return call_user_func_array([$this, $genericMethod], $args); - } - } - throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); - } - - public function reset() - { - $this->records = []; - $this->recordsByLevel = []; - } -} diff --git a/vendor/psr/log/composer.json b/vendor/psr/log/composer.json index ca056953..879fc6f5 100644 --- a/vendor/psr/log/composer.json +++ b/vendor/psr/log/composer.json @@ -11,16 +11,16 @@ } ], "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } } } diff --git a/vendor/topthink/think-image/src/Image.php b/vendor/topthink/think-image/src/Image.php index b7301ba0..e28a13d7 100644 --- a/vendor/topthink/think-image/src/Image.php +++ b/vendor/topthink/think-image/src/Image.php @@ -140,31 +140,6 @@ class Image return $this; } - /** - * http输出图片 - * @return void - */ - public function output() - { - $type = $this->info['type']; - header("content-type: image/{$type}"); - switch ($type) { - case 'png': - imagepng($this->im); - break; - case 'gif': - imagegif($this->im); - break; - case 'jpeg': - imagejpeg($this->im); - break; - case 'wbmp': - imagewbmp($this->im); - break; - } - exit; - } - /** * 返回图像宽度 * @return int 图像宽度 @@ -282,17 +257,16 @@ class Image public function crop($w, $h, $x = 0, $y = 0, $width = null, $height = null) { //设置保存尺寸 - empty($width) && $width = $w; empty($height) && $height = $h; do { //创建新图像 - $img = imagecreatetruecolor((int) $width, (int) $height); + $img = imagecreatetruecolor($width, $height); // 调整默认颜色 $color = imagecolorallocate($img, 255, 255, 255); imagefill($img, 0, 0, $color); //裁剪 - imagecopyresampled($img, $this->im, 0, 0, (int) $x, (int) $y, (int) $width, (int) $height, $w, $h); + imagecopyresampled($img, $this->im, 0, 0, $x, $y, $width, $height, $w, $h); imagedestroy($this->im); //销毁原图 //设置新图像 $this->im = $img; @@ -328,18 +302,18 @@ class Image $scale = min($width / $w, $height / $h); //设置缩略图的坐标及宽度和高度 $x = $y = 0; - $width = (int) ($w * $scale); - $height = (int) ($h * $scale); + $width = $w * $scale; + $height = $h * $scale; break; /* 居中裁剪 */ case self::THUMB_CENTER: //计算缩放比例 $scale = max($width / $w, $height / $h); //设置缩略图的坐标及宽度和高度 - $w = (int) ($width / $scale); - $h = (int) ($height / $scale); - $x = (int) (($this->info['width'] - $w) / 2); - $y = (int) (($this->info['height'] - $h) / 2); + $w = $width / $scale; + $h = $height / $scale; + $x = ($this->info['width'] - $w) / 2; + $y = ($this->info['height'] - $h) / 2; break; /* 左上角裁剪 */ case self::THUMB_NORTHWEST: @@ -347,18 +321,18 @@ class Image $scale = max($width / $w, $height / $h); //设置缩略图的坐标及宽度和高度 $x = $y = 0; - $w = (int) ($width / $scale); - $h = (int) ($height / $scale); + $w = $width / $scale; + $h = $height / $scale; break; /* 右下角裁剪 */ case self::THUMB_SOUTHEAST: //计算缩放比例 $scale = max($width / $w, $height / $h); //设置缩略图的坐标及宽度和高度 - $w = (int) ($width / $scale); - $h = (int) ($height / $scale); - $x = (int) ($this->info['width'] - $w); - $y = (int) ($this->info['height'] - $h); + $w = $width / $scale; + $h = $height / $scale; + $x = $this->info['width'] - $w; + $y = $this->info['height'] - $h; break; /* 填充 */ case self::THUMB_FILLED: @@ -369,12 +343,12 @@ class Image $scale = min($width / $w, $height / $h); } //设置缩略图的坐标及宽度和高度 - $neww = (int) ($w * $scale); - $newh = (int) ($h * $scale); + $neww = $w * $scale; + $newh = $h * $scale; $x = $this->info['width'] - $w; $y = $this->info['height'] - $h; - $posx = (int) (($width - $w * $scale) / 2); - $posy = (int) (($height - $h * $scale) / 2); + $posx = ($width - $w * $scale) / 2; + $posy = ($height - $h * $scale) / 2; do { //创建新图像 $img = imagecreatetruecolor($width, $height); @@ -403,9 +377,9 @@ class Image /** * 添加水印 * - * @param string $source 水印图片路径 - * @param int|array $locate 水印位置 - * @param int $alpha 透明度 + * @param string $source 水印图片路径 + * @param int $locate 水印位置 + * @param int $alpha 透明度 * @return $this */ public function water($source, $locate = self::WATER_SOUTHEAST, $alpha = 100) @@ -483,10 +457,10 @@ class Image // 调整默认颜色 $color = imagecolorallocate($src, 255, 255, 255); imagefill($src, 0, 0, $color); - imagecopy($src, $this->im, 0, 0, (int) $x, (int) $y, $info[0], $info[1]); + imagecopy($src, $this->im, 0, 0, $x, $y, $info[0], $info[1]); imagecopy($src, $water, 0, 0, 0, 0, $info[0], $info[1]); - imagecopymerge($this->im, $src, (int) $x, (int) $y, 0, 0, $info[0], $info[1], $alpha); - //销毁临时图片资源 + imagecopymerge($this->im, $src, $x, $y, 0, 0, $info[0], $info[1], $alpha); + //销毁零时图片资源 imagedestroy($src); } while (!empty($this->gif) && $this->gifNext()); //销毁水印资源 @@ -497,13 +471,13 @@ class Image /** * 图像添加文字 * - * @param string $text 添加的文字 - * @param string $font 字体路径 - * @param integer $size 字号 - * @param string $color 文字颜色 - * @param int|array $locate 文字写入位置 - * @param integer|array $offset 文字相对当前位置的偏移量 - * @param integer $angle 文字倾斜角度 + * @param string $text 添加的文字 + * @param string $font 字体路径 + * @param integer $size 字号 + * @param string $color 文字颜色 + * @param int $locate 文字写入位置 + * @param integer $offset 文字相对当前位置的偏移量 + * @param integer $angle 文字倾斜角度 * * @return $this * @throws ImageException @@ -585,24 +559,6 @@ class Image $offset = intval($offset); $ox = $oy = $offset; } - /* 图片黑白检测 */ - if ("auto" == $color) { - //X方向采集宽度:单英文字符占据宽度约为字体大小/1.6,单中文字符占据宽度约为字体大小*4/3;Y方向采集宽度:英文字符高度约为字体大小,中文会高一些。 - //使用保守宽度,以免在纯英文情况下采集区域超出图像范围,并且精度完全可以满足本功能。 - $pickX = intval(mb_strwidth($text) * ($size / 1.6)); - $pickY = $size; - - $brightness = 0; - for ($i = $x + $ox; $i < $pickX + $x + $ox; $i++) { - //根据文字基线确定要进行遍历的像素 - for ($j = $y + $oy - $pickY; $j < $y + $oy; $j++) { - //基线修正 - $brightness += self::getBrightnessOfPixel($i, $j); - } - } - - $color = $brightness / ($pickX * $pickY) > 127 ? '#00000000' : '#ffffffff'; - } /* 设置颜色 */ if (is_string($color) && 0 === strpos($color, '#')) { $color = str_split(substr($color, 1), 2); @@ -621,22 +577,6 @@ class Image return $this; } - /** - * 获取图片指定像素点的亮度值 - */ - private function getBrightnessOfPixel($x, $y) - { - $rgb = imagecolorat($this->im, $x, $y); - $r = ($rgb >> 16) & 0xFF; - $g = ($rgb >> 8) & 0xFF; - $b = $rgb & 0xFF; - - //红绿蓝能量不同,亮度不同,对应系数也不同(参考https://www.w3.org/TR/AERT/#color-contrast) - $brightness = intval($r * 0.299 + $g * 0.587 + $b * 0.114); - - return $brightness; - } - /** * 切换到GIF的下一帧并保存当前帧 */ diff --git a/vendor/topthink/think-image/src/image/Exception.php b/vendor/topthink/think-image/src/image/Exception.php index c35c71b9..2ebafd8c 100644 --- a/vendor/topthink/think-image/src/image/Exception.php +++ b/vendor/topthink/think-image/src/image/Exception.php @@ -11,7 +11,8 @@ namespace think\image; + class Exception extends \RuntimeException { -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/src/image/gif/Decoder.php b/vendor/topthink/think-image/src/image/gif/Decoder.php index 95fa2b84..a5630926 100644 --- a/vendor/topthink/think-image/src/image/gif/Decoder.php +++ b/vendor/topthink/think-image/src/image/gif/Decoder.php @@ -11,6 +11,7 @@ namespace think\image\gif; + class Decoder { public $GIF_buffer = []; @@ -203,4 +204,4 @@ class Decoder { return ($this->GIF_delays); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/src/image/gif/Encoder.php b/vendor/topthink/think-image/src/image/gif/Encoder.php index 763a1fd8..688f7a0a 100644 --- a/vendor/topthink/think-image/src/image/gif/Encoder.php +++ b/vendor/topthink/think-image/src/image/gif/Encoder.php @@ -48,7 +48,7 @@ class Encoder for ($i = 0; $i < count($GIF_src); $i++) { if (strtolower($GIF_mod) == "url") { $this->BUF[] = fread(fopen($GIF_src[$i], "rb"), filesize($GIF_src[$i])); - } elseif (strtolower($GIF_mod) == "bin") { + } else if (strtolower($GIF_mod) == "bin") { $this->BUF[] = $GIF_src[$i]; } else { printf("%s: %s ( %s )!", $this->VER, $this->ERR['ERR02'], $GIF_mod); @@ -74,7 +74,7 @@ class Encoder } $this->addHeader(); for ($i = 0; $i < count($this->BUF); $i++) { - isset($GIF_dly[$i]) && $this->addFrames($i, $GIF_dly[$i]); + $this->addFrames($i, $GIF_dly[$i]); } $this->addFooter(); } @@ -219,4 +219,4 @@ class Encoder { return ($this->GIF); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/src/image/gif/Gif.php b/vendor/topthink/think-image/src/image/gif/Gif.php index f803db0c..b8909158 100644 --- a/vendor/topthink/think-image/src/image/gif/Gif.php +++ b/vendor/topthink/think-image/src/image/gif/Gif.php @@ -85,4 +85,4 @@ class Gif $gif = new Encoder($this->frames, $this->delays, 0, 2, 0, 0, 0, 'bin'); file_put_contents($pathname, $gif->getAnimation()); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/tests/CropTest.php b/vendor/topthink/think-image/tests/CropTest.php index e2676307..26a05313 100644 --- a/vendor/topthink/think-image/tests/CropTest.php +++ b/vendor/topthink/think-image/tests/CropTest.php @@ -64,4 +64,4 @@ class CropTest extends TestCase @unlink($pathname); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/tests/FlipTest.php b/vendor/topthink/think-image/tests/FlipTest.php index d91d3af9..bf7a5e26 100644 --- a/vendor/topthink/think-image/tests/FlipTest.php +++ b/vendor/topthink/think-image/tests/FlipTest.php @@ -27,6 +27,7 @@ class FlipTest extends TestCase @unlink($pathname); } + public function testGif() { $pathname = TEST_PATH . 'tmp/flip.gif'; @@ -39,4 +40,4 @@ class FlipTest extends TestCase @unlink($pathname); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/tests/InfoTest.php b/vendor/topthink/think-image/tests/InfoTest.php index 97ff4861..22132ca4 100644 --- a/vendor/topthink/think-image/tests/InfoTest.php +++ b/vendor/topthink/think-image/tests/InfoTest.php @@ -37,6 +37,7 @@ class InfoTest extends TestCase $this->assertEquals([800, 600], $image->size()); } + public function testPng() { $image = Image::open($this->getPng()); @@ -56,4 +57,4 @@ class InfoTest extends TestCase $this->assertEquals('image/gif', $image->mime()); $this->assertEquals([380, 216], $image->size()); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/tests/RotateTest.php b/vendor/topthink/think-image/tests/RotateTest.php index 202062d3..56572d27 100644 --- a/vendor/topthink/think-image/tests/RotateTest.php +++ b/vendor/topthink/think-image/tests/RotateTest.php @@ -39,4 +39,4 @@ class RotateTest extends TestCase @unlink($pathname); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/tests/TestCase.php b/vendor/topthink/think-image/tests/TestCase.php index 3fd401ce..89ba1b45 100644 --- a/vendor/topthink/think-image/tests/TestCase.php +++ b/vendor/topthink/think-image/tests/TestCase.php @@ -30,4 +30,4 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase { return new File(TEST_PATH . 'images/test.gif'); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/tests/TextTest.php b/vendor/topthink/think-image/tests/TextTest.php index 87f04674..04506a27 100644 --- a/vendor/topthink/think-image/tests/TextTest.php +++ b/vendor/topthink/think-image/tests/TextTest.php @@ -55,4 +55,4 @@ class TextTest extends TestCase @unlink($pathname); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/tests/ThumbTest.php b/vendor/topthink/think-image/tests/ThumbTest.php index 98354c45..07113c8e 100644 --- a/vendor/topthink/think-image/tests/ThumbTest.php +++ b/vendor/topthink/think-image/tests/ThumbTest.php @@ -103,6 +103,7 @@ class ThumbTest extends TestCase @unlink($pathname); } + public function testPng() { $pathname = TEST_PATH . 'tmp/thumb.png'; @@ -280,4 +281,4 @@ class ThumbTest extends TestCase @unlink($pathname); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/tests/WaterTest.php b/vendor/topthink/think-image/tests/WaterTest.php index 7c2c1dac..b6a2bcce 100644 --- a/vendor/topthink/think-image/tests/WaterTest.php +++ b/vendor/topthink/think-image/tests/WaterTest.php @@ -55,4 +55,4 @@ class WaterTest extends TestCase @unlink($pathname); } -} +} \ No newline at end of file diff --git a/vendor/topthink/think-image/tests/autoload.php b/vendor/topthink/think-image/tests/autoload.php index d21954b2..f2e8aaef 100644 --- a/vendor/topthink/think-image/tests/autoload.php +++ b/vendor/topthink/think-image/tests/autoload.php @@ -12,4 +12,4 @@ define('TEST_PATH', __DIR__ . '/'); // 加载框架基础文件 require __DIR__ . '/../thinkphp/base.php'; \think\Loader::addNamespace('tests', TEST_PATH); -\think\Loader::addNamespace('think', __DIR__ . '/../src/'); +\think\Loader::addNamespace('think', __DIR__ . '/../src/'); \ No newline at end of file From 28023be44ae981f22f27cace9f3a96724651b64b Mon Sep 17 00:00:00 2001 From: wangjinlei <751475802@qq.com> Date: Thu, 4 Jun 2026 16:50:31 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=B7=BB=E5=8A=A0reviewer=E7=9A=84?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=A1=E7=A8=BF=E6=97=B6=E5=80=99=E7=AD=9B?= =?UTF-8?q?=E9=80=89=E7=9A=84=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/api/controller/Reviewer.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/api/controller/Reviewer.php b/application/api/controller/Reviewer.php index a031a419..a76e5ebd 100644 --- a/application/api/controller/Reviewer.php +++ b/application/api/controller/Reviewer.php @@ -2269,7 +2269,8 @@ class Reviewer extends Base $where['t_user.email'] = ['like',"%" . $data["email"] . "%"]; } if(isset($data['field'])&&$data['field']!=''){ - $where['t_user_reviewer_info.field'] = ['like',"%" . $data["field"] . "%"]; + // field 参数同时匹配原始 field 与 AI 总结 field_ai + $where['t_user_reviewer_info.field|t_user_reviewer_info.field_ai'] = ['like',"%" . $data["field"] . "%"]; } if (isset($data['major_id'])&&$data['major_id']!=0){ $where['t_user_reviewer_info.major'] = ['in',$this->majorids($data['major_id'])]; @@ -2306,7 +2307,7 @@ class Reviewer extends Base $list = $this->reviewer_to_journal_obj ->join("t_user", "t_user.user_id = t_reviewer_to_journal.reviewer_id", "left") ->join("t_user_reviewer_info", "t_user_reviewer_info.reviewer_id = t_reviewer_to_journal.reviewer_id", "left") - ->field('t_user.account,t_user.email,t_user.realname,t_user_reviewer_info.company,t_user_reviewer_info.field,t_user_reviewer_info.last_invite_time,t_user.user_id,t_user.rs_num') + ->field('t_user.account,t_user.email,t_user.realname,t_user_reviewer_info.company,t_user_reviewer_info.field,t_user_reviewer_info.field_ai,t_user_reviewer_info.last_invite_time,t_user.user_id,t_user.rs_num') ->where($where)->where(function($query) use ($iTeenDaysLater) { $query->where('t_user_reviewer_info.last_invite_time', '<', $iTeenDaysLater) ->whereOr('t_user_reviewer_info.last_invite_time', '=', 0); From 93d25de09455441606ccda39014a1f81b8719c8b Mon Sep 17 00:00:00 2001 From: wangjinlei <751475802@qq.com> Date: Fri, 5 Jun 2026 11:01:16 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E5=AE=8C=E5=96=84expert=E9=A2=86=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/api/controller/ExpertFieldAi.php | 161 ++++++++++ application/api/controller/ExpertManage.php | 31 +- application/api/job/ExpertFieldAiFill.php | 38 +++ application/common/ExpertFieldAiService.php | 321 +++++++++++++++++++ application/common/UserFieldAiService.php | 14 + sql/add_field_ai_to_expert.sql | 6 + 6 files changed, 558 insertions(+), 13 deletions(-) create mode 100644 application/api/controller/ExpertFieldAi.php create mode 100644 application/api/job/ExpertFieldAiFill.php create mode 100644 application/common/ExpertFieldAiService.php create mode 100644 sql/add_field_ai_to_expert.sql diff --git a/application/api/controller/ExpertFieldAi.php b/application/api/controller/ExpertFieldAi.php new file mode 100644 index 00000000..4ad58f33 --- /dev/null +++ b/application/api/controller/ExpertFieldAi.php @@ -0,0 +1,161 @@ +request->param('force', 0)) === 1; + $delay = max(0, intval($this->request->param('delay', 1))); + + $svc = new ExpertFieldAiService(); + $started = $svc->startLinkChain($force, $delay); + + return jsonSuccess([ + 'started' => $started, + 'queue' => ExpertFieldAiService::QUEUE_NAME, + 'force' => $force, + 'msg' => $started ? 'link chain enqueued' : 'no pending experts', + ]); + } + + /** + * 同步关联单个 expert + */ + public function linkOne() + { + $expertId = intval($this->request->param('expert_id', 0)); + $force = intval($this->request->param('force', 0)) === 1; + if ($expertId <= 0) { + return jsonError('expert_id required'); + } + + $svc = new ExpertFieldAiService(); + $result = $svc->linkFromUser($expertId, $force); + if (empty($result['ok'])) { + return jsonError(isset($result['error']) ? $result['error'] : 'failed'); + } + return jsonSuccess($result); + } + + /** + * 同步批量关联 + * expert_ids: 逗号分隔,或传 limit 扫描待处理前 N 条 + */ + public function linkBatch() + { + $force = intval($this->request->param('force', 0)) === 1; + $idsRaw = trim((string)$this->request->param('expert_ids', '')); + $limit = min(max(intval($this->request->param('limit', 0)), 0), 200); + + $ids = []; + if ($idsRaw !== '') { + $ids = array_filter(array_map('intval', explode(',', $idsRaw))); + } elseif ($limit > 0) { + $ids = Db::name('expert') + ->where('state', '<>', 5) + ->where(function ($q) { + $q->where('field_ai_status', ExpertFieldAiService::STATUS_PENDING) + ->whereOr('field_ai_status', ExpertFieldAiService::STATUS_FAILED); + }) + ->order('expert_id asc') + ->limit($limit) + ->column('expert_id'); + } + + if (empty($ids)) { + return jsonError('expert_ids 或 limit 必填'); + } + + $svc = new ExpertFieldAiService(); + $result = $svc->batchLinkFromUser($ids, $force); + return jsonSuccess($result); + } + + /** + * user 更新 field_ai 后,同步到同邮箱 expert + */ + public function syncByUser() + { + $userId = intval($this->request->param('user_id', 0)); + $force = intval($this->request->param('force', 0)) === 1; + if ($userId <= 0) { + return jsonError('user_id required'); + } + + $svc = new ExpertFieldAiService(); + $result = $svc->syncExpertsByUserId($userId, $force); + if (empty($result['ok'])) { + return jsonError(isset($result['error']) ? $result['error'] : 'failed'); + } + return jsonSuccess($result); + } + + /** + * 预览是否可关联 + */ + public function preview() + { + $expertId = intval($this->request->param('expert_id', 0)); + if ($expertId <= 0) { + return jsonError('expert_id required'); + } + + $svc = new ExpertFieldAiService(); + $result = $svc->previewLink($expertId); + if (empty($result['ok'])) { + return jsonError(isset($result['error']) ? $result['error'] : 'failed'); + } + + $result['field_ai_status_text'] = $svc->statusLabel(intval($result['expert_field_ai_status'])); + return jsonSuccess($result); + } + + /** + * 统计 field_ai 覆盖 + */ + public function statistics() + { + $total = Db::name('expert')->where('state', '<>', 5)->count(); + $done = Db::name('expert')->where('state', '<>', 5)->where('field_ai_status', ExpertFieldAiService::STATUS_DONE)->count(); + $userLink = Db::name('expert') + ->where('state', '<>', 5) + ->where('field_ai_source', ExpertFieldAiService::SOURCE_USER_LINK) + ->count(); + $noUserLink = Db::name('expert') + ->where('state', '<>', 5) + ->where('field_ai_status', ExpertFieldAiService::STATUS_NO_USER_LINK) + ->count(); + $pending = Db::name('expert') + ->where('state', '<>', 5) + ->where('field_ai_status', ExpertFieldAiService::STATUS_PENDING) + ->count(); + + return jsonSuccess([ + 'total' => $total, + 'done' => $done, + 'user_link' => $userLink, + 'no_user_link' => $noUserLink, + 'pending' => $pending, + 'coverage_rate' => $total > 0 ? round($done / $total * 100, 2) . '%' : '0%', + ]); + } +} diff --git a/application/api/controller/ExpertManage.php b/application/api/controller/ExpertManage.php index 87992b2c..bd3a0fea 100644 --- a/application/api/controller/ExpertManage.php +++ b/application/api/controller/ExpertManage.php @@ -44,17 +44,22 @@ class ExpertManage extends Base $query = Db::name('expert')->alias('e'); $countQuery = Db::name('expert')->alias('e'); - $needJoin = ($field !== ''); - if ($needJoin) { - $query->join('t_expert_field ef', 'ef.expert_id = e.expert_id AND ef.state = 0', 'inner'); - $countQuery->join('t_expert_field ef', 'ef.expert_id = e.expert_id AND ef.state = 0', 'inner'); - if ($field !== '') { - $query->where('ef.field', 'like', '%' . $field . '%'); - $countQuery->where('ef.field', 'like', '%' . $field . '%'); - } - $query->group('e.expert_id'); - $countQuery->group('e.expert_id'); + if ($field !== '') { + $fieldExpertIds = Db::name('expert_field') + ->where('state', 0) + ->where('field', 'like', '%' . $field . '%') + ->column('expert_id'); + $fieldExpertIds = array_values(array_unique(array_filter(array_map('intval', $fieldExpertIds)))); + + $fieldWhere = function ($q) use ($field, $fieldExpertIds) { + $q->where('e.field_ai', 'like', '%' . $field . '%'); + if (!empty($fieldExpertIds)) { + $q->whereOr('e.expert_id', 'in', $fieldExpertIds); + } + }; + $query->where($fieldWhere); + $countQuery->where($fieldWhere); } if ($state !== '-1' && $state !== '') { @@ -62,8 +67,8 @@ class ExpertManage extends Base $countQuery->where('e.state', intval($state)); } if ($keyword !== '') { - $query->where('e.name|e.email|e.affiliation', 'like', '%' . $keyword . '%'); - $countQuery->where('e.name|e.email|e.affiliation', 'like', '%' . $keyword . '%'); + $query->where('e.name|e.email|e.affiliation|e.field_ai', 'like', '%' . $keyword . '%'); + $countQuery->where('e.name|e.email|e.affiliation|e.field_ai', 'like', '%' . $keyword . '%'); } if ($source !== '') { $query->where('e.source', $source); @@ -72,7 +77,7 @@ class ExpertManage extends Base // $countQuery = clone $query; // $total = $countQuery->distinct('e.expert_id')->count(); - $total = $needJoin ? count($countQuery->group('e.expert_id')->column('e.expert_id')) : $countQuery->count(); + $total = $countQuery->count(); $list = $query ->field('e.*') diff --git a/application/api/job/ExpertFieldAiFill.php b/application/api/job/ExpertFieldAiFill.php new file mode 100644 index 00000000..d86afe12 --- /dev/null +++ b/application/api/job/ExpertFieldAiFill.php @@ -0,0 +1,38 @@ + 0 && $mode === 'link') { + $svc->linkFromUser($expertId, $force); + } + + $job->delete(); + + $delay = max(0, (int)(isset($data['delay']) ? $data['delay'] : 1)); + $svc->enqueueNextLink($delay, $queue, $expertId, $force); + } +} diff --git a/application/common/ExpertFieldAiService.php b/application/common/ExpertFieldAiService.php new file mode 100644 index 00000000..eae654d0 --- /dev/null +++ b/application/common/ExpertFieldAiService.php @@ -0,0 +1,321 @@ +logFile = ROOT_PATH . 'runtime' . DS . 'expert_field_ai.log'; + } + + /** + * 启动链式关联(从 expert_id=0 之后找下一位待处理专家)。 + */ + public function startLinkChain($force = false, $delay = 1, $queue = '') + { + return $this->enqueueNextLink($delay, $queue, 0, $force); + } + + /** + * 链式:处理 expert_id > $afterExpertId 的下一位。 + */ + public function enqueueNextLink($delay = 1, $queue = '', $afterExpertId = 0, $force = false) + { + if ($queue === '') { + $queue = self::QUEUE_NAME; + } + $afterExpertId = intval($afterExpertId); + $expertId = $this->findNextLinkExpertId($afterExpertId, $force); + if ($expertId <= 0) { + $this->log('[ExpertFieldAi] link chain finished after expert_id=' . $afterExpertId); + return false; + } + + $data = [ + 'expert_id' => $expertId, + 'queue' => $queue, + 'force' => $force ? 1 : 0, + 'mode' => 'link', + ]; + $jobClass = 'app\\api\\job\\ExpertFieldAiFill@fire'; + if ($delay > 0) { + Queue::later($delay, $jobClass, $data, $queue); + } else { + Queue::push($jobClass, $data, $queue); + } + $this->log('[ExpertFieldAi] enqueued expert_id=' . $expertId . ' queue=' . $queue); + return true; + } + + /** + * 单个 expert:尝试从 user 邮箱关联 field_ai。 + * + * @return array{ok:bool, linked?:bool, skipped?:bool, field_ai?:string, user_id?:int, error?:string} + */ + public function linkFromUser($expertId, $force = false) + { + $expertId = intval($expertId); + if ($expertId <= 0) { + return ['ok' => false, 'error' => 'invalid expert_id']; + } + + $expert = Db::name('expert')->where('expert_id', $expertId)->find(); + if (!$expert) { + return ['ok' => false, 'error' => 'expert not found']; + } + + if (!$force + && intval($expert['field_ai_status']) === self::STATUS_DONE + && trim((string)$expert['field_ai']) !== '') { + return [ + 'ok' => true, + 'skipped' => true, + 'field_ai' => (string)$expert['field_ai'], + 'source' => (string)($expert['field_ai_source'] ?? ''), + ]; + } + + $email = strtolower(trim((string)($expert['email'] ?? ''))); + if ($email === '') { + $this->updateFieldAi($expertId, '', self::STATUS_NO_USER_LINK, '', 'no email'); + return ['ok' => true, 'linked' => false, 'reason' => 'empty email']; + } + + $user = Db::name('user') + ->where('email', $email) + ->where('state', 0) + ->field('user_id,email,realname') + ->find(); + + if (!$user) { + $this->updateFieldAi($expertId, '', self::STATUS_NO_USER_LINK, '', 'no matching user'); + return ['ok' => true, 'linked' => false, 'reason' => 'user not found']; + } + + $uri = Db::name('user_reviewer_info') + ->where('reviewer_id', intval($user['user_id'])) + ->where('state', 0) + ->find(); + + $fieldAi = $uri ? trim((string)($uri['field_ai'] ?? '')) : ''; + $userStatus = $uri ? intval($uri['field_ai_status']) : 0; + + if ($fieldAi === '' || $userStatus !== UserFieldAiService::STATUS_DONE) { + $this->updateFieldAi($expertId, '', self::STATUS_NO_USER_LINK, '', 'user field_ai not ready'); + return [ + 'ok' => true, + 'linked' => false, + 'user_id' => intval($user['user_id']), + 'reason' => 'user has no field_ai', + ]; + } + + $this->updateFieldAi($expertId, $fieldAi, self::STATUS_DONE, self::SOURCE_USER_LINK, 'linked from user_id=' . $user['user_id']); + return [ + 'ok' => true, + 'linked' => true, + 'field_ai' => $fieldAi, + 'user_id' => intval($user['user_id']), + 'source' => self::SOURCE_USER_LINK, + ]; + } + + /** + * 批量同步(同步执行,适合小批量调试)。 + */ + public function batchLinkFromUser(array $expertIds, $force = false) + { + $linked = 0; + $skipped = 0; + $noLink = 0; + $failed = 0; + $details = []; + + foreach ($expertIds as $expertId) { + $expertId = intval($expertId); + if ($expertId <= 0) { + continue; + } + $result = $this->linkFromUser($expertId, $force); + if (empty($result['ok'])) { + $failed++; + } elseif (!empty($result['skipped'])) { + $skipped++; + } elseif (!empty($result['linked'])) { + $linked++; + } else { + $noLink++; + } + $details[] = array_merge(['expert_id' => $expertId], $result); + } + + return [ + 'total' => count($details), + 'linked' => $linked, + 'skipped' => $skipped, + 'no_link' => $noLink, + 'failed' => $failed, + 'details' => $details, + ]; + } + + /** + * 预览:expert 是否可关联到 user.field_ai。 + */ + public function previewLink($expertId) + { + $expertId = intval($expertId); + $expert = Db::name('expert')->where('expert_id', $expertId)->find(); + if (!$expert) { + return ['ok' => false, 'error' => 'expert not found']; + } + + $email = strtolower(trim((string)($expert['email'] ?? ''))); + $user = null; + $uri = null; + if ($email !== '') { + $user = Db::name('user')->where('email', $email)->where('state', 0)->field('user_id,email,realname')->find(); + if ($user) { + $uri = Db::name('user_reviewer_info') + ->where('reviewer_id', intval($user['user_id'])) + ->where('state', 0) + ->find(); + } + } + + $canLink = $user && $uri + && trim((string)($uri['field_ai'] ?? '')) !== '' + && intval($uri['field_ai_status']) === UserFieldAiService::STATUS_DONE; + + return [ + 'ok' => true, + 'expert_id' => $expertId, + 'expert_email' => $email, + 'expert_field_ai' => (string)($expert['field_ai'] ?? ''), + 'expert_field_ai_status'=> intval($expert['field_ai_status'] ?? 0), + 'matched_user_id' => $user ? intval($user['user_id']) : 0, + 'matched_user_name' => $user ? (string)$user['realname'] : '', + 'user_field_ai' => $uri ? (string)($uri['field_ai'] ?? '') : '', + 'user_field_ai_status' => $uri ? intval($uri['field_ai_status']) : 0, + 'can_link' => $canLink, + ]; + } + + /** + * user 生成 field_ai 后,反向同步到同邮箱 expert(可选调用)。 + */ + public function syncExpertsByUserId($userId, $force = false) + { + $userId = intval($userId); + $user = Db::name('user')->where('user_id', $userId)->where('state', 0)->field('user_id,email')->find(); + if (!$user || trim((string)$user['email']) === '') { + return ['ok' => false, 'error' => 'user not found']; + } + + $email = strtolower(trim((string)$user['email'])); + $expertIds = Db::name('expert') + ->where('email', $email) + ->where('state', '<>', 5) + ->column('expert_id'); + + if (empty($expertIds)) { + return ['ok' => true, 'synced' => 0, 'msg' => 'no expert with same email']; + } + + return array_merge(['ok' => true], $this->batchLinkFromUser($expertIds, $force)); + } + + private function findNextLinkExpertId($afterExpertId, $force) + { + $batch = 50; + $cursor = intval($afterExpertId); + + while (true) { + $query = Db::name('expert') + ->where('expert_id', '>', $cursor) + ->where('state', '<>', 5); + + if (!$force) { + $query->where(function ($q) { + $q->where('field_ai_status', self::STATUS_PENDING) + ->whereOr('field_ai_status', self::STATUS_FAILED); + }); + } + + $ids = $query->order('expert_id asc')->limit($batch)->column('expert_id'); + if (empty($ids)) { + return 0; + } + + foreach ($ids as $expertId) { + $expertId = intval($expertId); + $cursor = $expertId; + + if (!$force) { + $row = Db::name('expert')->where('expert_id', $expertId)->field('field_ai,field_ai_status')->find(); + if ($row + && intval($row['field_ai_status']) === self::STATUS_DONE + && trim((string)$row['field_ai']) !== '') { + continue; + } + } + + return $expertId; + } + } + } + + private function updateFieldAi($expertId, $fieldAi, $status, $source, $note) + { + $data = [ + 'field_ai' => mb_substr(trim((string)$fieldAi), 0, 512), + 'field_ai_status' => intval($status), + 'field_ai_utime' => time(), + 'field_ai_source' => mb_substr(trim((string)$source), 0, 32), + ]; + Db::name('expert')->where('expert_id', intval($expertId))->update($data); + if ($note !== '') { + $this->log('[ExpertFieldAi] expert_id=' . $expertId . ' status=' . $status . ' note=' . $note); + } + } + + public function statusLabel($status) + { + $map = [ + self::STATUS_PENDING => 'pending', + self::STATUS_DONE => 'done', + self::STATUS_INSUFFICIENT => 'insufficient', + self::STATUS_FAILED => 'failed', + self::STATUS_NO_USER_LINK => 'no_user_link', + ]; + return isset($map[$status]) ? $map[$status] : 'unknown'; + } + + public function log($msg) + { + $line = date('Y-m-d H:i:s') . ' ' . $msg . PHP_EOL; + @file_put_contents($this->logFile, $line, FILE_APPEND); + } +} diff --git a/application/common/UserFieldAiService.php b/application/common/UserFieldAiService.php index 3bc023ec..20cee6bc 100644 --- a/application/common/UserFieldAiService.php +++ b/application/common/UserFieldAiService.php @@ -102,6 +102,7 @@ class UserFieldAiService throw new Exception('LLM returned empty field'); } $this->updateFieldAi($userId, $fieldAi, self::STATUS_DONE, ''); + $this->syncLinkedExperts($userId); return ['ok' => true, 'field_ai' => $fieldAi]; } catch (\Throwable $e) { $this->updateFieldAi($userId, '', self::STATUS_FAILED, mb_substr($e->getMessage(), 0, 500)); @@ -460,4 +461,17 @@ class UserFieldAiService $line = date('Y-m-d H:i:s') . ' ' . $msg . PHP_EOL; @file_put_contents($this->logFile, $line, FILE_APPEND); } + + /** + * user.field_ai 更新后,同步到同邮箱 expert(方案 C 关联)。 + */ + private function syncLinkedExperts($userId) + { + try { + $svc = new ExpertFieldAiService(); + $svc->syncExpertsByUserId(intval($userId), true); + } catch (\Throwable $e) { + $this->log('[FieldAi] sync expert fail user_id=' . $userId . ' ' . $e->getMessage()); + } + } } diff --git a/sql/add_field_ai_to_expert.sql b/sql/add_field_ai_to_expert.sql new file mode 100644 index 00000000..0f2b8547 --- /dev/null +++ b/sql/add_field_ai_to_expert.sql @@ -0,0 +1,6 @@ +-- Expert 主领域 AI 总结(方案 C:先邮箱关联 user.field_ai,后续可 AI 补全) +ALTER TABLE `t_expert` + ADD COLUMN `field_ai` VARCHAR(512) NOT NULL DEFAULT '' COMMENT 'AI/关联总结的主要研究领域(中文)' AFTER `affiliation`, + ADD COLUMN `field_ai_status` TINYINT NOT NULL DEFAULT 0 COMMENT '0待处理 1已生成 2资料不足 3失败 4无user关联待AI' AFTER `field_ai`, + ADD COLUMN `field_ai_utime` INT NOT NULL DEFAULT 0 COMMENT 'field_ai 更新时间' AFTER `field_ai_status`, + ADD COLUMN `field_ai_source` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '来源: user_link / ai' AFTER `field_ai_utime`;