mirror of
https://github.com/jkjoy/Typecho-Theme-Puock.git
synced 2026-06-13 14:44:15 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b416e58596 | |||
| 5d67235be0 | |||
| 134bb0d66c | |||
| 8afb1934dd | |||
| b73e465a79 | |||
| ae990a722b | |||
| 35806ea2fb | |||
| 3bb53afcea | |||
| 9f389fbb34 | |||
| 4c1c795dc2 | |||
| b3b1407647 | |||
| 6b9cc81754 | |||
| c313e29d99 | |||
| 33bc5d53f5 | |||
| 58b2e80883 | |||
| c7efd5abd6 | |||
| 28a72cb279 | |||
| 5abd899791 | |||
| e604c6b4d3 | |||
| 074af5187a |
@@ -18,23 +18,42 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var countdown = 5; // 设置倒计时时间(秒)
|
||||
var countdownElement = document.getElementById('time-count-down');
|
||||
|
||||
// 检查是否在 PJAX 环境中
|
||||
var isPjax = typeof window.Pjax !== 'undefined' ||
|
||||
(typeof jQuery !== 'undefined' && jQuery.pjax);
|
||||
// 更新倒计时显示
|
||||
function updateCountdown() {
|
||||
countdownElement.textContent = countdown;
|
||||
countdown--;
|
||||
|
||||
countdown--;
|
||||
if (countdown < 0) {
|
||||
// 倒计时结束,跳转到首页
|
||||
window.location.href = "<?php $this->options->siteUrl(); ?>";
|
||||
var homeUrl = "<?php $this->options->siteUrl(); ?>";
|
||||
|
||||
if (isPjax) {
|
||||
// 使用 PJAX 方式跳转
|
||||
if (typeof window.Pjax !== 'undefined') {
|
||||
// 使用原生 PJAX
|
||||
var pjax = new Pjax();
|
||||
pjax.loadUrl(homeUrl);
|
||||
} else if (typeof jQuery !== 'undefined' && jQuery.pjax) {
|
||||
// 使用 jQuery PJAX
|
||||
$.pjax({url: homeUrl, container: '[data-pjax-container]'});
|
||||
}
|
||||
} else {
|
||||
// 普通跳转
|
||||
window.location.href = homeUrl;
|
||||
}
|
||||
} else {
|
||||
// 继续倒计时
|
||||
setTimeout(updateCountdown, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 开始倒计时
|
||||
updateCountdown();
|
||||
// 如果是 PJAX 加载,需要手动执行一些初始化
|
||||
if (isPjax && typeof window.puockInit !== 'undefined') {
|
||||
window.puockInit();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
|
||||
@@ -20,6 +20,49 @@
|
||||
4. 主题部分功能需要安装插件`Puock`。项目地址: [Puock Plugin](https://github.com/jkjoy/typecho-plugin-puock)
|
||||
5. 友情链接功能需要使用插件`Links`。项目地址: [Links Plugin](https://file.imsun.org/upload/2025-06/Links-1.2.7.zip),首页友情链接需要在`友链分类`中添加分类`home`。
|
||||
|
||||
### 主题配置
|
||||
### 更新
|
||||
|
||||
- 全部标签页面需要启用php的 `iconv` 扩展
|
||||
- 2025.07.05
|
||||
- 1.1.3
|
||||
- 兼容typecho 1.3.0版本
|
||||
- 修复pjax黑暗模式下闪烁问题
|
||||
- 修复说说页面的pjax加载问题
|
||||
- 修复上一篇与下一篇文章的链接
|
||||
- 增加归属地显示设置
|
||||
- 增加系统显示设置
|
||||
- 增加浏览器信息显示设置
|
||||
- 修复pjax模式下的404跳转
|
||||
|
||||
- 2025.07.07 表情短代码解析集成
|
||||
- 1.1.5
|
||||
|
||||
- 将表情短代码(如 :smile:)自动解析为图片表情,集成到主题评论内容输出中。
|
||||
|
||||
- 2025.07.08
|
||||
|
||||
- 1.1.6
|
||||
|
||||
- 修复头像区域背景的bug
|
||||
- 优化php8.3兼容性
|
||||
|
||||
- 1.1.8
|
||||
|
||||
- 优化github链接的正则表达式,只解析主仓库链接(如 https://github.com/用户名/仓库名),不再解析子路径(如 /tree/、/blob/ 等)为卡片。
|
||||
- 实现文章内容图片懒加载,自动将常见图片格式(jpg、jpeg、png、webp)的<img>标签替换为带懒加载属性的格式。
|
||||
- 修正getPostCover函数,确保只从原始内容中提取第一张真实图片地址,不受懒加载替换影响,避免首页首图变成load.svg。
|
||||
|
||||
- 新增支持[success]、[primary]、[danger]、[warning]、[info]、[dark]等alert类短代码,自动渲染为对应的Bootstrap风格提示框。
|
||||
- 新增支持[collapse title='xxx']内容[/collapse]折叠面板短代码,自动渲染为带唯一ID的Bootstrap折叠结构。
|
||||
- 新增支持[download file='xxx.zip' size='12MB']文件地址[/download]下载短代码,自动渲染为带文件名、大小、声明和下载地址的下载信息块。
|
||||
- 新增支持[reply]隐藏内容[/reply]回复可见短代码,未满足条件时前端显示提示,已评论且审核通过后显示隐藏内容。
|
||||
|
||||
- 1.2.0
|
||||
|
||||
- 新增支持首页登录
|
||||
|
||||
- 2025.08.02
|
||||
|
||||
- 1.2.2
|
||||
|
||||
- 增加侧边栏显示的全局开关
|
||||
- 修复代码块中的短代码解析问题
|
||||
+15
-5
@@ -3,10 +3,16 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
$this->need('header.php');
|
||||
?>
|
||||
<div class="row row-cols-1">
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<div class="col-lg-8 col-md-12 animated fadeInLeft ">
|
||||
<div class="animated fadeInLeft ">
|
||||
<?php else: ?>
|
||||
<div class="col-lg-12 col-md-12">
|
||||
<div class="row box-plr15">
|
||||
<?php endif; ?>
|
||||
<div> <!--文章列表-->
|
||||
<div id="posts">
|
||||
<?php if ($this->options->listmodel): ?>
|
||||
<div class=" mr-0 ml-0">
|
||||
<?php while ($this->next()): ?>
|
||||
<?php
|
||||
@@ -61,21 +67,25 @@ $coverImage = getPostCover($this->content, $this->cid);
|
||||
</article>
|
||||
<?php endwhile; ?>
|
||||
</div>
|
||||
<div class="mt20 p-flex-s-right" data-no-instant>
|
||||
<?php $this->pageNav('«', '»', 1, '...', array(
|
||||
<div class="mt20 p-flex-s-right" data-no-instant>
|
||||
<?php $this->pageNav('«', '»', 1, '...', array(
|
||||
'wrapTag' => 'ul',
|
||||
'wrapClass' => 'pagination comment-ajax-load',
|
||||
'itemTag' => 'li',
|
||||
'textTag' => 'span',
|
||||
'currentClass' => 'active',
|
||||
'currentClass' => 'cur',
|
||||
'prevClass' => 'prev',
|
||||
'nextClass' => 'next'
|
||||
)); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php $this->need('card.php'); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
|
||||
<?php endif; ?>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
File diff suppressed because one or more lines are too long
+80
-43
@@ -74,9 +74,6 @@ class Puock {
|
||||
this.registerModeChangeEvent()
|
||||
this.eventCommentPageChangeEvent()
|
||||
this.eventCommentPreSubmit()
|
||||
this.eventSmiley()
|
||||
this.eventOpenCommentBox()
|
||||
this.eventCloseCommentBox()
|
||||
this.eventSendPostLike()
|
||||
this.eventPostMainBoxResize()
|
||||
this.swiperOnceEvent()
|
||||
@@ -84,6 +81,8 @@ class Puock {
|
||||
this.detectDevice()
|
||||
window.addEventListener('resize', ()=>this.detectDevice());
|
||||
layer.config({shade: 0.5})
|
||||
// 新增:首次加载时初始化评论相关事件
|
||||
this.initCommentEvents();
|
||||
}
|
||||
|
||||
pageInit() {
|
||||
@@ -144,6 +143,10 @@ class Puock {
|
||||
});
|
||||
// form ajax submit
|
||||
$(document).on("submit", ".ajax-form", (e) => {
|
||||
// 如果是登录弹窗表单,允许原生提交
|
||||
if ($(e.target).attr('id') === 'front-login-form') {
|
||||
return true;
|
||||
}
|
||||
e.preventDefault();
|
||||
const form = $(this.ct(e));
|
||||
const formEls = form.find(":input")
|
||||
@@ -209,6 +212,32 @@ class Puock {
|
||||
}
|
||||
return false;
|
||||
})
|
||||
// 登录弹窗表单AJAX提交(集成Puock插件接口)
|
||||
$(document).off('submit', '#front-login-form');
|
||||
$(document).on('submit', '#front-login-form', function(e) {
|
||||
e.preventDefault();
|
||||
var $form = $(this);
|
||||
var data = $form.serialize();
|
||||
$.ajax({
|
||||
url: '/index.php/ajaxlogin/',
|
||||
type: 'POST',
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if (res.success) {
|
||||
window.Puock.toast(res.msg || '登录成功', TYPE_SUCCESS);
|
||||
setTimeout(function() {
|
||||
window.location.reload();
|
||||
}, 800);
|
||||
} else {
|
||||
window.Puock.toast(res.msg || '登录失败', TYPE_DANGER);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
window.Puock.toast('请求失败', TYPE_DANGER);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pageLinkBlankOpenInit() {
|
||||
@@ -416,7 +445,7 @@ class Puock {
|
||||
this.pageLinkBlankOpenInit()
|
||||
this.initGithubCard();
|
||||
this.keyUpHandle();
|
||||
this.loadHitokoto();
|
||||
// this.loadHitokoto();
|
||||
// this.asyncCacheViews();
|
||||
this.swiperInit();
|
||||
// this.validateInit();
|
||||
@@ -465,6 +494,8 @@ class Puock {
|
||||
// $('#post-main, #sidebar').theiaStickySidebar({
|
||||
// additionalMarginTop: 20
|
||||
// });
|
||||
// 新增:pjax切换后重新初始化评论相关事件
|
||||
this.initCommentEvents();
|
||||
}
|
||||
|
||||
|
||||
@@ -770,12 +801,14 @@ class Puock {
|
||||
}
|
||||
|
||||
parseFormData(formEl, args = {}) {
|
||||
// 先获取表单所有字段
|
||||
const dataArr = formEl.serializeArray();
|
||||
const data = {...args};
|
||||
const data = {};
|
||||
for (let i = 0; i < dataArr.length; i++) {
|
||||
data[dataArr[i].name] = dataArr[i].value;
|
||||
}
|
||||
return jQuery.param(data);
|
||||
// 合并额外参数
|
||||
return jQuery.param(Object.assign(data, args));
|
||||
}
|
||||
|
||||
eventCommentPreSubmit() {
|
||||
@@ -879,33 +912,49 @@ class Puock {
|
||||
}
|
||||
|
||||
eventOpenCommentBox() {
|
||||
$(document).on("click", "[id^=comment-reply-]", (e) => {
|
||||
this.data.comment.replyId = $(this.ct(e)).attr("data-id");
|
||||
if ($.trim(this.data.comment.replyId) === '') {
|
||||
this.toast('结构有误', TYPE_DANGER);
|
||||
$(document).off("click", ".comment-reply");
|
||||
$(document).on("click", ".comment-reply", function(e) {
|
||||
e.preventDefault();
|
||||
const replyBtn = $(e.currentTarget);
|
||||
const replyId = replyBtn.attr("data-coid");
|
||||
if ($.trim(replyId) === '') {
|
||||
window.Puock.toast('结构有误', TYPE_DANGER);
|
||||
return;
|
||||
}
|
||||
const cf = $("#comment-form"),
|
||||
cb = $("#comment-box-" + this.data.comment.replyId);
|
||||
cf.addClass("box-sw");
|
||||
cb.removeClass("d-none").append(cf);
|
||||
const cf = $("#comment-form");
|
||||
const commentLi = replyBtn.closest('.post-comment');
|
||||
// 只在表单在原位时插入占位符
|
||||
if (!$("#comment-form-place-holder").length && cf.parent().attr("id") === "comment-form-box") {
|
||||
cf.before('<div id="comment-form-place-holder"></div>');
|
||||
}
|
||||
// 每次都append到目标评论下方
|
||||
commentLi.append(cf);
|
||||
$("#comment-cancel").removeClass("d-none");
|
||||
$("#comment").val("");
|
||||
$("#comment_parent").val(this.data.comment.replyId);
|
||||
})
|
||||
|
||||
$("#comment_parent").val(replyId);
|
||||
window.Puock.data.comment.replyId = replyId;
|
||||
// 滚动至表单
|
||||
if (cf.length && cf[0].scrollIntoView) {
|
||||
cf[0].scrollIntoView({behavior: "smooth", block: "center"});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
eventCloseCommentBox() {
|
||||
$(document).off("click", "#comment-cancel");
|
||||
$(document).on("click", "#comment-cancel", () => {
|
||||
const cf = $("#comment-form"),
|
||||
cb = $("#comment-box-" + this.data.comment.replyId);
|
||||
cf.removeClass("box-sw");
|
||||
cb.addClass("d-none");
|
||||
$("#comment-form-box").append(cf);
|
||||
const cf = $("#comment-form");
|
||||
const holder = $("#comment-form-place-holder");
|
||||
if (holder.length) {
|
||||
holder.before(cf);
|
||||
holder.remove();
|
||||
} else {
|
||||
$("#comment-form-box").append(cf);
|
||||
}
|
||||
$("#comment-cancel").addClass("d-none");
|
||||
this.data.comment.replyId = null;
|
||||
})
|
||||
$("#comment_parent").val('');
|
||||
});
|
||||
}
|
||||
|
||||
eventSendPostLike() {
|
||||
@@ -949,11 +998,12 @@ class Puock {
|
||||
}
|
||||
|
||||
eventSmiley() {
|
||||
$(document).off('click', '.smiley-img');
|
||||
$(document).on('click', '.smiley-img', (e) => {
|
||||
const comment = $("#comment");
|
||||
comment.val(comment.val() + ' ' + $(this.ct(e)).attr("data-id") + ' ');
|
||||
layer.closeAll();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
startLoading() {
|
||||
@@ -1135,25 +1185,6 @@ class Puock {
|
||||
});
|
||||
}
|
||||
|
||||
loadHitokoto() {
|
||||
setTimeout(() => {
|
||||
$(".widget-puock-hitokoto").each((_, v) => {
|
||||
const el = $(v);
|
||||
const api = el.attr("data-api") || "https://v1.hitokoto.cn/"
|
||||
$.get(api, (res) => {
|
||||
el.find(".t").text(res.hitokoto ?? res.content ?? "无内容");
|
||||
el.find('.f').text(res.from);
|
||||
el.find('.fb').removeClass("d-none");
|
||||
}, 'json').fail((err) => {
|
||||
console.error(err)
|
||||
el.find(".t").text("加载失败:" + err.responseText || err);
|
||||
el.remove(".fb");
|
||||
})
|
||||
})
|
||||
}, 300)
|
||||
}
|
||||
|
||||
|
||||
toast(msg, type = TYPE_PRIMARY, options = {}) {
|
||||
options = Object.assign({
|
||||
duration: 2600,
|
||||
@@ -1171,6 +1202,12 @@ class Puock {
|
||||
return t;
|
||||
}
|
||||
|
||||
// 新增:统一初始化评论相关事件
|
||||
initCommentEvents() {
|
||||
this.eventOpenCommentBox();
|
||||
this.eventCloseCommentBox();
|
||||
this.eventSmiley();
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
|
||||
@@ -63,4 +63,20 @@ $colors = ['bg-primary', 'bg-secondary', 'bg-success', 'bg-danger', 'bg-warning'
|
||||
</div>
|
||||
</article>
|
||||
<?php endwhile; ?>
|
||||
<?php
|
||||
$pageprev = $this->options->pageprev ?? '0';
|
||||
if ($pageprev == '1' && $this->have()):
|
||||
?>
|
||||
<div class="mt20 p-flex-s-right" data-no-instant>
|
||||
<?php $this->pageNav('«', '»', 1, '...', array(
|
||||
'wrapTag' => 'ul',
|
||||
'wrapClass' => 'pagination comment-ajax-load',
|
||||
'itemTag' => 'li',
|
||||
'textTag' => 'span',
|
||||
'currentClass' => 'cur',
|
||||
'prevClass' => 'prev',
|
||||
'nextClass' => 'next'
|
||||
)); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
+53
-129
@@ -21,7 +21,6 @@
|
||||
<input type="text" value="1" hidden name="comment-logged" id="comment-logged">
|
||||
<div class="col-12">
|
||||
<p class="t-sm c-sub">登录身份: <a href="<?php $this->options->profileUrl(); ?>"><?php $this->user->screenName(); ?></a> . <a href="<?php $this->options->logoutUrl(); ?>" title="Logout">登出 »</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
@@ -44,9 +43,9 @@
|
||||
<div>
|
||||
<?php if(!$this->user->hasLogin()): ?>
|
||||
<div class="d-inline-block">
|
||||
<a class="btn btn-primary btn-ssm" href="<?php $this->options->loginUrl(); ?>" title="登录" target="_blank">
|
||||
<i class="fa fa-right-to-bracket"></i> 登录
|
||||
</a>
|
||||
<button class="btn btn-primary btn-ssm pk-modal-toggle" type="button" data-once-load="true" data-id="front-login" title="快捷登录" data-url="<?php echo get_correct_url('/login/'); ?>">
|
||||
<i class="fa fa-right-to-bracket"></i> 快捷登录
|
||||
</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@@ -60,6 +59,7 @@
|
||||
<i class="fa-regular fa-face-smile t-md"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<input type="hidden" name="parent" id="comment_parent" value="">
|
||||
<button type="submit" id="comment-submit" class="btn btn-primary btn-ssm">
|
||||
<i class="fa-regular fa-paper-plane"></i> 发布评论
|
||||
</button>
|
||||
@@ -69,49 +69,49 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="comment-ajax-load" class="text-center mt20 d-none">
|
||||
<div class="pk-skeleton _comment">
|
||||
<div class="_h">
|
||||
<div class="_avatar"></div>
|
||||
<div class="_info">
|
||||
<div class="_name"></div>
|
||||
<div class="_date"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_text">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pk-skeleton _comment">
|
||||
<div class="_h">
|
||||
<div class="_avatar"></div>
|
||||
<div class="_info">
|
||||
<div class="_name"></div>
|
||||
<div class="_date"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_text">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pk-skeleton _comment">
|
||||
<div class="_h">
|
||||
<div class="_avatar"></div>
|
||||
<div class="_info">
|
||||
<div class="_name"></div>
|
||||
<div class="_date"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_text">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pk-skeleton _comment">
|
||||
<div class="_h">
|
||||
<div class="_avatar"></div>
|
||||
<div class="_info">
|
||||
<div class="_name"></div>
|
||||
<div class="_date"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_text">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pk-skeleton _comment">
|
||||
<div class="_h">
|
||||
<div class="_avatar"></div>
|
||||
<div class="_info">
|
||||
<div class="_name"></div>
|
||||
<div class="_date"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_text">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pk-skeleton _comment">
|
||||
<div class="_h">
|
||||
<div class="_avatar"></div>
|
||||
<div class="_info">
|
||||
<div class="_name"></div>
|
||||
<div class="_date"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_text">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="mt20 alert alert-warning">
|
||||
<i class="fa fa-exclamation-circle"></i> 评论已关闭
|
||||
@@ -172,21 +172,15 @@
|
||||
</div>
|
||||
<div class="t-sm c-sub">
|
||||
<span><?php $comments->date('Y-m-d H:i:s'); ?></span>
|
||||
<?php $comments->reply(
|
||||
sprintf('<a onclick="return TypechoComment.reply(\'%s\', %d);" class="hide-info animated bounceIn c-sub-a t-sm ml-1 comment-reply" href="%s#comment-%d"><span class="comment-reply-text"><i class="fa fa-share-from-square"></i>回复</span></a>',
|
||||
$comments->theId,
|
||||
$comments->coid,
|
||||
$comments->commentUrl($comments->coid),
|
||||
$comments->coid)
|
||||
); ?>
|
||||
<a rel="nofollow" class="hide-info animated bounceIn c-sub-a t-sm ml-1 comment-reply" href="javascript:void(0);" data-coid="<?php echo $comments->coid; ?>"><span class="comment-reply-text"><i class="fa fa-share-from-square"></i>回复</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="content-text t-md mt10 puock-text">
|
||||
<?php if ($comments->parent) {echo getPermalinkFromCoid($comments->parent);} $comments->content();?>
|
||||
<?php if ($comments->parent) {echo getPermalinkFromCoid($comments->parent);} echo parse_smiley_shortcode($comments->content);?>
|
||||
<div class="comment-os c-sub">
|
||||
<?php
|
||||
<?php
|
||||
$deviceInfo = getBrowsersInfo($comments->agent);
|
||||
$icons = getDeviceIcon($deviceInfo);
|
||||
?>
|
||||
@@ -199,7 +193,6 @@
|
||||
: '未知系统';
|
||||
?>
|
||||
</span>
|
||||
|
||||
<!-- 浏览器信息 -->
|
||||
<span class="mt10" title="<?php echo $deviceInfo['browser'] . ' ' . $deviceInfo['version']; ?>">
|
||||
<?php echo $icons['browser']; ?>
|
||||
@@ -209,12 +202,12 @@
|
||||
: '未知浏览器';
|
||||
?>
|
||||
</span>
|
||||
|
||||
<!-- IP 地理位置 -->
|
||||
<?php if($comments->ip): ?>
|
||||
<span class="mt10" title="IP">
|
||||
<i class="fa-solid fa-location-dot"></i> <?php echo get_ip_region($comments->ip); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -224,73 +217,4 @@
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<script>
|
||||
var TypechoComment = {
|
||||
dom : function (id) {
|
||||
return document.getElementById(id);
|
||||
},
|
||||
|
||||
create : function (tag, attr) {
|
||||
var el = document.createElement(tag);
|
||||
|
||||
for (var key in attr) {
|
||||
el.setAttribute(key, attr[key]);
|
||||
}
|
||||
|
||||
return el;
|
||||
},
|
||||
|
||||
reply : function (cid, coid) {
|
||||
var comment = this.dom(cid), parent = comment.parentNode,
|
||||
response = this.dom('comment-form-box'), holder = response.parentNode,
|
||||
form = this.dom('comment-form'), textarea = this.dom('comment'),
|
||||
submit = this.dom('comment-submit'),
|
||||
cancel = this.dom('comment-cancel');
|
||||
|
||||
if (null != this.dom('comment-form-place-holder')) {
|
||||
var holder = this.dom('comment-form-place-holder');
|
||||
}
|
||||
|
||||
// 移动评论表单
|
||||
if (null == this.dom('comment-form-place-holder')) {
|
||||
var holder = this.create('div', {
|
||||
'id': 'comment-form-place-holder'
|
||||
});
|
||||
|
||||
response.parentNode.insertBefore(holder, response);
|
||||
}
|
||||
|
||||
// 修改表单中的内容
|
||||
comment.appendChild(response);
|
||||
|
||||
// 显示取消回复按钮
|
||||
cancel.style.display = '';
|
||||
|
||||
// 记录回复的评论ID
|
||||
this.dom('comment-parent').value = coid;
|
||||
|
||||
// 移动光标到评论框
|
||||
textarea.focus();
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
cancelReply : function () {
|
||||
var response = this.dom('comment-form-box'), holder = this.dom('comment-form-place-holder'),
|
||||
comment = this.dom('comment-parent');
|
||||
|
||||
if (null != holder) {
|
||||
holder.parentNode.insertBefore(response, holder);
|
||||
holder.parentNode.removeChild(holder);
|
||||
}
|
||||
|
||||
// 隐藏取消回复按钮
|
||||
this.dom('comment-cancel').style.display = 'none';
|
||||
// 清除回复评论ID
|
||||
comment.value = '';
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<?php } ?>
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"overtrue/pinyin": "^4.1"
|
||||
}
|
||||
}
|
||||
Generated
+92
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1d353c32c45b49f759b47ca83156c5c1",
|
||||
"packages": [
|
||||
{
|
||||
"name": "overtrue/pinyin",
|
||||
"version": "4.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/overtrue/pinyin.git",
|
||||
"reference": "4d0fb4f27f0c79e81c9489e0c0ae4a4f8837eae7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/overtrue/pinyin/zipball/4d0fb4f27f0c79e81c9489e0c0ae4a4f8837eae7",
|
||||
"reference": "4d0fb4f27f0c79e81c9489e0c0ae4a4f8837eae7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"brainmaestro/composer-git-hooks": "^2.7",
|
||||
"friendsofphp/php-cs-fixer": "^2.16",
|
||||
"phpunit/phpunit": "~8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"hooks": {
|
||||
"pre-push": [
|
||||
"composer test",
|
||||
"composer check-style"
|
||||
],
|
||||
"pre-commit": [
|
||||
"composer test",
|
||||
"composer fix-style"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/const.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Overtrue\\Pinyin\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "overtrue",
|
||||
"email": "anzhengchao@gmail.com",
|
||||
"homepage": "http://github.com/overtrue"
|
||||
}
|
||||
],
|
||||
"description": "Chinese to pinyin translator.",
|
||||
"homepage": "https://github.com/overtrue/pinyin",
|
||||
"keywords": [
|
||||
"Chinese",
|
||||
"Pinyin",
|
||||
"cn2pinyin"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/overtrue/pinyin/issues",
|
||||
"source": "https://github.com/overtrue/pinyin/tree/4.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/overtrue",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-27T10:17:12+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
+2
-3
@@ -68,7 +68,7 @@
|
||||
© <?php echo date('Y'); ?> <a href="<?php $this->options->siteUrl(); ?>"><?php $this->options->title(); ?></a>
|
||||
<div class="fs12 mt10 c-sub">
|
||||
<span> Theme by
|
||||
<a target="_blank" class="c-sub" title="Puock v2.8.14" href="https://github.com/jkjoy/typecho-theme-puock">Puock</a>
|
||||
<a target="_blank" class="c-sub" title="Puock v1.2.2" href="https://github.com/jkjoy/typecho-theme-puock">Puock</a>
|
||||
</span>
|
||||
<span> Powered by
|
||||
<a target="_blank" class="c-sub" title="Typecho" href="https://typecho.org">Typecho</a> <p><a target="_blank" class="c-sub" title="老孙博客" href="https://imsun.org">老孙博客</a>制作</p>
|
||||
@@ -85,7 +85,7 @@
|
||||
"home": "<?php $this->options->siteUrl(); ?>",
|
||||
"use_post_menu": true,
|
||||
"is_single": false,
|
||||
"is_pjax": false,
|
||||
"is_pjax": true,
|
||||
"main_lazy_img": true,
|
||||
"link_blank_open": true,
|
||||
//"async_view_id": null,
|
||||
@@ -102,5 +102,4 @@
|
||||
<script type="text/javascript" data-no-instant src="<?php $this->options->themeUrl('assets/js/puock.js'); ?>" id="puock-js"></script>
|
||||
<?php $this->footer(); ?>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+752
-350
File diff suppressed because it is too large
Load Diff
+56
-14
@@ -4,23 +4,26 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta http-equiv='content-language' content='zh_CN'>
|
||||
<title><?php $this->archiveTitle([
|
||||
'category' => _t('分类 %s 下的文章'),
|
||||
'search' => _t('包含关键字 %s 的文章'),
|
||||
'tag' => _t('标签 %s 下的文章'),
|
||||
'author' => _t('%s 发布的文章')
|
||||
], '', ' - '); ?><?php $this->options->title(); ?></title>
|
||||
], '', ' - '); ?>
|
||||
<?php $this->options->title(); ?>
|
||||
<?php if ($this->is('index')) echo ' - '; ?>
|
||||
<?php if ($this->is('index')) $this->options->description() ?>
|
||||
</title>
|
||||
<link rel="canonical" href="<?php $this->options->siteUrl(); ?>">
|
||||
<meta name='robots' content='max-image-preview:large' />
|
||||
<?php $this->options->addhead(); ?>
|
||||
<style id='puock-inline-css' type='text/css'>
|
||||
body {
|
||||
--pk-c-primary: <?php if ($this->options->primaryColor): ?><?php $this->options->primaryColor() ?><?php else: ?>#A7E6F4<?php endif; ?>;
|
||||
--pk-c-primary: <?php if ($this->options->primaryColor): ?><?php $this->options->primaryColor() ?><?php else: ?>#A7E6F4<?php endif; ?> !important;
|
||||
}
|
||||
:root {
|
||||
--puock-block-not-tran: <?php if ($this->options->blockNotTransparent): ?><?php $this->options->blockNotTransparent() ?><?php else: ?>100<?php endif; ?>%;
|
||||
--puock-block-not-tran: <?php if ($this->options->blockNotTransparent): ?><?php $this->options->blockNotTransparent() ?><?php else: ?>100<?php endif; ?>% !important;
|
||||
}
|
||||
</style>
|
||||
<?php if ($this->options->icoUrl): ?>
|
||||
@@ -32,7 +35,6 @@
|
||||
<!-- 通过自有函数输出HTML头部信息 -->
|
||||
<?php $this->header(); ?>
|
||||
</head>
|
||||
|
||||
<body class="puock-auto custom-background">
|
||||
<div>
|
||||
<div id="header-box" class="animated fadeInDown"></div>
|
||||
@@ -73,7 +75,7 @@
|
||||
</li>
|
||||
<?php \Widget\Contents\Page\Rows::alloc()->to($pages); ?>
|
||||
<?php while ($pages->next()): ?>
|
||||
<li <?php if ($this->is('page', $pages->slug)): ?> class='current-menu-item current_page_item menu-current<?php endif; ?> menu-item menu-item-type-post_type menu-item-object-page '>
|
||||
<li class="menu-item menu-item-type-post_type menu-item-object-page<?php if ($this->is('page', $pages->slug)) echo ' current-menu-item current_page_item menu-current'; ?>">
|
||||
<a class='ww'
|
||||
href="<?php $pages->permalink(); ?>"
|
||||
title="<?php $pages->title(); ?>">
|
||||
@@ -81,6 +83,15 @@
|
||||
</a>
|
||||
</li>
|
||||
<?php endwhile; ?>
|
||||
<?php if($this->user->hasLogin()): ?>
|
||||
<li>
|
||||
<a data-bs-toggle="tooltip" title="用户中心" href="/admin" target="_blank">
|
||||
<img alt="用户中心" src="<?php $stats = get_site_statistics();echo $stats['avatar']; ?>" class="min-avatar">
|
||||
</a>
|
||||
</li>
|
||||
<?php else: ?>
|
||||
<li><a data-no-instant data-bs-toggle="tooltip" title="登入" data-title="登入" href="javascript:void(0)" class="pk-modal-toggle" data-once-load="true" data-url="<?php echo get_correct_url('/login/'); ?>"><i class="fa fa-right-to-bracket"></i></a></li>
|
||||
<?php endif; ?>
|
||||
<li><a class="colorMode" data-bs-toggle="tooltip" title="模式切换" href="javascript:void(0)"><i class="fa fa-circle-half-stroke"></i></a></li>
|
||||
<li><a class="search-modal-btn" data-bs-toggle="tooltip" title="搜索" href="javascript:void(0)"><i class="fa fa-search"></i></a></li>
|
||||
</ul>
|
||||
@@ -128,8 +139,8 @@
|
||||
<?php endwhile; ?>
|
||||
<li class='menu-item menu-item-type-post_type menu-item-object-page'>
|
||||
<span><a href="#">分类</a>
|
||||
<a href="#menu-sub-689" data-bs-toggle="collapse"><i class="fa fa-chevron-down t-sm ml-1 menu-sub-icon"></i></a></span>
|
||||
<ul id="menu-sub-689" class="sub-menu collapse">
|
||||
<a href="#menu" data-bs-toggle="collapse"><i class="fa fa-chevron-down t-sm ml-1 menu-sub-icon"></i></a></span>
|
||||
<ul id="menu" class="sub-menu collapse">
|
||||
<?php $categories = Typecho_Widget::widget('Widget_Metas_Category_List'); ?>
|
||||
<?php while($categories->next()): ?>
|
||||
<li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-child">
|
||||
@@ -141,6 +152,15 @@
|
||||
</li>
|
||||
<?php endwhile; ?>
|
||||
</ul>
|
||||
<?php if($this->user->hasLogin()): ?>
|
||||
<li>
|
||||
<a data-bs-toggle="tooltip" title="用户中心" href="/admin" target="_blank">
|
||||
<img alt="用户中心" src="<?php $stats = get_site_statistics();echo $stats['avatar']; ?>" class="min-avatar">
|
||||
</a>
|
||||
</li>
|
||||
<?php else: ?>
|
||||
<li><a data-no-instant data-bs-toggle="tooltip" title="登入" data-title="登入" href="javascript:void(0)" class="pk-modal-toggle" data-once-load="true" data-url="<?php echo get_correct_url('/login/'); ?>"><i class="fa fa-right-to-bracket"></i></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -157,12 +177,34 @@
|
||||
<div data-swiper="init" data-swiper-class="global-top-notice-swiper" data-swiper-args='{"direction":"vertical","autoplay":{"delay":3000,"disableOnInteraction":false},"loop":true}'>
|
||||
<div class="swiper global-top-notice-swiper">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-slide t-line-1">
|
||||
<a class="ta3" data-no-instant href="javascript:void(0)">
|
||||
<span class="notice-icon"><i class="fa-regular fa-bell"></i></span>
|
||||
<span><?php $this->options->gonggao(); ?></span>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
// 获取公告内容
|
||||
$gonggao = $this->options->gonggao;
|
||||
if ($gonggao) {
|
||||
// 按行分割
|
||||
$lines = explode("\n", $gonggao);
|
||||
foreach ($lines as $line) {
|
||||
$parts = explode('|', $line);
|
||||
// 只处理格式正确的行
|
||||
if (count($parts) >= 3) {
|
||||
$title = trim($parts[0]);
|
||||
$url = trim($parts[1]);
|
||||
$icon = trim($parts[2]);
|
||||
// 链接为空时使用 javascript:void(0)
|
||||
$href = $url !== '' ? htmlspecialchars($url) : 'javascript:void(0)';
|
||||
// 图标为空时使用默认
|
||||
$icon_class = $icon !== '' ? $icon : 'fa-regular fa-bell';
|
||||
// 输出 HTML
|
||||
echo '<div class="swiper-slide t-line-1">';
|
||||
echo '<a class="ta3" data-no-instant href="' . $href . '">';
|
||||
echo '<span class="notice-icon"><i class="' . $icon_class . '"></i></span>';
|
||||
echo '<span>' . htmlspecialchars($title) . '</span>';
|
||||
echo '</a>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Pouck theme for Typecho
|
||||
*
|
||||
* 老孙移植
|
||||
*
|
||||
* @package Typecho Pouck Theme
|
||||
* @author 老孙博客
|
||||
* @version 1.0.4
|
||||
* @version 1.2.2
|
||||
* @link http://www.imsun.org
|
||||
*/
|
||||
|
||||
@@ -13,8 +14,13 @@ $this->need('header.php');
|
||||
$this->need('sticky.php');
|
||||
?>
|
||||
<div class="row row-cols-1">
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<div class="col-lg-8 col-md-12 animated fadeInLeft ">
|
||||
<div class="animated fadeInLeft ">
|
||||
<?php else: ?>
|
||||
<div class="col-lg-12 col-md-12">
|
||||
<div class="row box-plr15">
|
||||
<?php endif; ?>
|
||||
<div> <!--文章列表-->
|
||||
<div id="posts">
|
||||
<?php if ($this->options->listmodel): ?>
|
||||
@@ -82,7 +88,7 @@ if ($pageprev == '1' && $this->have()):
|
||||
'wrapClass' => 'pagination comment-ajax-load',
|
||||
'itemTag' => 'li',
|
||||
'textTag' => 'span',
|
||||
'currentClass' => 'active',
|
||||
'currentClass' => 'cur',
|
||||
'prevClass' => 'prev',
|
||||
'nextClass' => 'next'
|
||||
)); ?>
|
||||
@@ -96,5 +102,7 @@ if ($pageprev == '1' && $this->have()):
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
<?php endif; ?>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
+9
-17
@@ -15,32 +15,28 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
</nav>
|
||||
</div>
|
||||
<div id="page-archives">
|
||||
<div id="page" class="w-100">
|
||||
<div id="posts" class="animated fadeInLeft ">
|
||||
<div class="p-block puock-text">
|
||||
<div class="timeline no-style">
|
||||
<?php
|
||||
<div id="page" class="w-100">
|
||||
<div id="posts" class="animated fadeInLeft ">
|
||||
<div class="p-block puock-text">
|
||||
<div class="timeline no-style">
|
||||
<?php
|
||||
$stat = Typecho_Widget::widget('Widget_Stat');
|
||||
Typecho_Widget::widget('Widget_Contents_Post_Recent', 'pageSize=' . $stat->publishedPostsNum)->to($archives);
|
||||
$year = 0;
|
||||
$mon = 0;
|
||||
$output = '';
|
||||
|
||||
while ($archives->next()) {
|
||||
$year_tmp = date('Y', $archives->created);
|
||||
$mon_tmp = date('m', $archives->created);
|
||||
$day_tmp = date('d', $archives->created);
|
||||
|
||||
// 检查是否需要新的时间线项目
|
||||
if ($year != $year_tmp || $mon != $mon_tmp) {
|
||||
// 如果不是第一个项目,先关闭之前的ul
|
||||
if ($year > 0 && $mon > 0) {
|
||||
$output .= '</ul></div></div>';
|
||||
}
|
||||
|
||||
}
|
||||
$year = $year_tmp;
|
||||
$mon = $mon_tmp;
|
||||
|
||||
// 开始新的时间线项目
|
||||
$output .= '<div class="timeline-item">';
|
||||
$output .= '<div class="timeline-location"></div>';
|
||||
@@ -48,19 +44,15 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
$output .= '<h4>' . $year . '-' . $mon . '</h4>';
|
||||
$output .= '<ul class="pd-links pl-0">';
|
||||
}
|
||||
|
||||
// 输出文章项
|
||||
$output .= '<li><a title="' . $archives->title . '" href="' . $archives->permalink . '">';
|
||||
$output .= $archives->title . ' ( ' . $day_tmp . '日 )</a></li>';
|
||||
}
|
||||
|
||||
// 循环结束后关闭最后的标签
|
||||
if ($year > 0 && $mon > 0) {
|
||||
$output .= '</ul></div></div>';
|
||||
}
|
||||
|
||||
echo $output;
|
||||
?>
|
||||
</div> </div> </div> </div> </div>
|
||||
|
||||
<?php $this->need('footer.php'); ?>
|
||||
?>
|
||||
</div></div></div></div></div>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
+4
-1
@@ -14,7 +14,7 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
<div id="page-links">
|
||||
<div id="page">
|
||||
<div class="row row-cols-1">
|
||||
<div id="posts" class="col-12 animated fadeInLeft ">
|
||||
<div class="puock-text no-style">
|
||||
@@ -22,5 +22,8 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($this->allow('comment')): ?>
|
||||
<?php $this->need('comments.php'); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
+21
-37
@@ -7,44 +7,28 @@
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<?php $this->need('header.php'); ?>
|
||||
<div id="breadcrumb" class="animated fadeInUp">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a class="a-link" href="<?php $this->options->siteUrl(); ?>">首页</a></li>
|
||||
<li class="breadcrumb-item active " aria-current="page"><?php $this->title() ?></li>
|
||||
</ol>
|
||||
</nav>
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a class="a-link" href="<?php $this->options->siteUrl(); ?>">首页</a></li>
|
||||
<li class="breadcrumb-item active " aria-current="page"><?php $this->title() ?></li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
<div id="page-links">
|
||||
<div id="page" class="row row-cols-1">
|
||||
<div id="posts" class="col-12 animated fadeInLeft ">
|
||||
<div class="puock-text no-style">
|
||||
<div class="p-block links-main" id="page-links-217">
|
||||
<h6><?php $this->title() ?></h6>
|
||||
<div class="links-main-box row t-sm">
|
||||
<?php
|
||||
Links_Plugin::output('
|
||||
<a class="link-item a-link col-lg-3 col-md-4 col-sm-6 col-6"
|
||||
href="{url}"
|
||||
target="_blank" rel="me" title="{name}" data-bs-toggle="tooltip">
|
||||
<div class="clearfix puock-bg">
|
||||
<img alt="{name}"
|
||||
src="{image}"
|
||||
class="lazy md-avatar"
|
||||
data-src="{image}"
|
||||
alt="{name}">
|
||||
<div class="info">
|
||||
<p class="ml-1 text-nowrap text-truncate">{name}</p>
|
||||
<p class="c-sub ml-1 text-nowrap text-truncate">{title}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
');
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="page-links">
|
||||
<div id="page" class="row row-cols-1">
|
||||
<div id="posts" class="col-12 animated fadeInLeft ">
|
||||
<div class="puock-text no-style">
|
||||
<div class="p-block links-main" id="page-links-217">
|
||||
<h6><?php $this->title() ?></h6>
|
||||
<div class="links-main-box row t-sm">
|
||||
<?php Links_Plugin::output('<a class="link-item a-link col-lg-3 col-md-4 col-sm-6 col-6" href="{url}" target="_blank" rel="me" title="{name}" data-bs-toggle="tooltip"><div class="clearfix puock-bg"><img alt="{name}" src="{image}" class="lazy md-avatar" data-src="{image}" alt="{name}"><div class="info"><p class="ml-1 text-nowrap text-truncate">{name}</p><p class="c-sub ml-1 text-nowrap text-truncate">{title}</p></div></div></a>');?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($this->allow('comment')): ?>
|
||||
<?php $this->need('comments.php'); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
@@ -17,7 +17,11 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<?php $commenters = getAllCommenters(); ?>
|
||||
<div id="page-reads">
|
||||
<div id="page" class="row row-cols-1">
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<div id="posts" class="col-lg-8 col-md-12 animated fadeInLeft ">
|
||||
<?php else: ?>
|
||||
<div id="posts" class="col-lg-12 col-md-12">
|
||||
<?php endif; ?>
|
||||
<div class="p-block puock-text">
|
||||
<h2 class="t-lg"><?php $this->title() ?></h2>
|
||||
<div class="mt20 row pd-links">
|
||||
@@ -44,5 +48,7 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
<?php endif; ?>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
+62
-54
@@ -13,74 +13,71 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<li class="breadcrumb-item active " aria-current="page"><?php $this->title() ?></li>
|
||||
</ol>
|
||||
</nav>
|
||||
<div id="page-moments">
|
||||
<div id="page-moments">
|
||||
<div class="row">
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<div id="posts" class="col-lg-8 col-md-12 animated fadeInLeft ">
|
||||
<?php else: ?>
|
||||
<div id="posts" class="col-lg-12 col-md-12">
|
||||
<?php endif; ?>
|
||||
<?php $tooot = $this->fields->tooot ? $this->fields->tooot : 'https://www.imsun.org/toot.json'; ?>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/15.0.12/marked.min.js" integrity="sha512-rCQgmUulW6f6QegOvTntKKb5IAoxTpGVCdWqYjkXEpzAns6XUFs8NKVqWe+KQpctp/EoRSFSuykVputqknLYMg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.5/css/lightbox.min.css" integrity="sha512-xtV3HfYNbQXS/1R1jP53KbFcU9WXiSA1RFKzl5hRlJgdOJm4OxHCWYpskm6lN0xp0XtKGpAfVShpbvlFH3MDAA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.5/js/lightbox.min.js" integrity="sha512-KbRFbjA5bwNan6DvPl1ODUolvTTZ/vckssnFhka5cG80JVa5zSlRPCr055xSgU/q6oMIGhZWLhcbgIC0fyw3RQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<div id="tooot"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
let offset = 0; // 初始偏移量
|
||||
const limit = 20; // 每次加载的数量
|
||||
|
||||
function fetchAndDisplayToots() {
|
||||
let offset = 0;
|
||||
const limit = 20;
|
||||
function formatHTML(toots) {
|
||||
let htmlString = '';
|
||||
let htmlString = '';
|
||||
toots.forEach(toot => {
|
||||
// 判断是否存在 reblog
|
||||
const isReblog = toot.reblog && toot.reblog.content;
|
||||
const content = isReblog ? toot.reblog.content : toot.content;
|
||||
const url = isReblog ? toot.reblog.url : toot.url;
|
||||
const account = isReblog ? toot.reblog.account : toot.account;
|
||||
const created_at = isReblog ? toot.reblog.created_at : toot.created_at;
|
||||
const media_attachments = isReblog ? toot.reblog.media_attachments : toot.media_attachments;
|
||||
|
||||
let mediaHTML = ''; // 初始化资源相关HTML为空字符串
|
||||
// 处理媒体附件
|
||||
if (media_attachments.length > 0) {
|
||||
let mediaHTML = '';
|
||||
if (media_attachments && media_attachments.length > 0) {
|
||||
media_attachments.forEach(attachment => {
|
||||
if (attachment.type === 'image') {
|
||||
mediaHTML += `<a href="${attachment.url}" target="_blank" data-lightbox="image-set"><img src="${attachment.preview_url}" class="thumbnail-image img" ></a>`;
|
||||
}
|
||||
});
|
||||
}
|
||||
// 使用 marked 转换 markdown 内容为 HTML
|
||||
const htmlContent = marked.parse(content);
|
||||
// 创建 HTML 字符串
|
||||
const htmlContent = marked.parse(content || '');
|
||||
htmlString += `
|
||||
<div class="mb20 puock-text moments-item">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-1">
|
||||
<div class="col-12 col-md-1">
|
||||
<a class="meta ta3" href="${account.url}" target="_blank" rel="nofollow">
|
||||
<div class="avatar mb10">
|
||||
<img src='${account.avatar}'
|
||||
class='lazy md-avatar mt-1'
|
||||
data-src='${account.avatar}'
|
||||
alt="${account.display_name}" title="${account.display_name}">
|
||||
</div>
|
||||
<div class="t-line-1 info fs12">${account.display_name}</div>
|
||||
</a>
|
||||
<div class="avatar mb10">
|
||||
<img src='${account.avatar}'
|
||||
class='lazy md-avatar mt-1'
|
||||
data-src='${account.avatar}'
|
||||
alt="${account.display_name}" title="${account.display_name}">
|
||||
</div>
|
||||
<div class="col-12 col-md-11">
|
||||
<div class="p-block moment-content-box"> <span class="al"></span>
|
||||
<div class="mt10 moment-content entry-content show-link-icon">
|
||||
<p>${htmlContent}
|
||||
</p>
|
||||
<div class="resimg">${mediaHTML}</div>
|
||||
</div>
|
||||
<div class="mt10 moment-footer p-flex-s-right"> <span class="t-sm c-sub">
|
||||
<a class="c-sub-a" href="${url}">
|
||||
<span class="mr-2"><i class="fa-regular fa-clock mr-1"></i>${new Date(created_at).toLocaleString()}</span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="t-line-1 info fs12">${account.display_name}</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-md-11">
|
||||
<div class="p-block moment-content-box"> <span class="al"></span>
|
||||
<div class="mt10 moment-content entry-content show-link-icon">
|
||||
<p>${htmlContent}</p>
|
||||
<div class="resimg">${mediaHTML}</div>
|
||||
</div>
|
||||
<div class="mt10 moment-footer p-flex-s-right"> <span class="t-sm c-sub">
|
||||
<a class="c-sub-a" href="${url}">
|
||||
<span class="mr-2"><i class="fa-regular fa-clock mr-1"></i>${new Date(created_at).toLocaleString()}</span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
return htmlString;
|
||||
@@ -93,17 +90,26 @@ window.onload = function() {
|
||||
return [];
|
||||
});
|
||||
}
|
||||
function fetchAndDisplayToots() {
|
||||
fetchToots().then(data => {
|
||||
const memosContainer = document.getElementById('tooot');
|
||||
const tootsToShow = data.slice(offset, offset + limit); // 选择要显示的toots
|
||||
memosContainer.innerHTML += formatHTML(tootsToShow);
|
||||
});
|
||||
}
|
||||
// 在页面加载完成后获取并展示 toots
|
||||
fetchAndDisplayToots();
|
||||
};
|
||||
</script>
|
||||
const memosContainer = document.getElementById('tooot');
|
||||
if (memosContainer) memosContainer.innerHTML = '';
|
||||
fetchToots().then(data => {
|
||||
if (!Array.isArray(data)) {
|
||||
console.error('toot.json is not an array:', data);
|
||||
return;
|
||||
}
|
||||
const tootsToShow = data.slice(offset, offset + limit);
|
||||
if (memosContainer) memosContainer.innerHTML += formatHTML(tootsToShow);
|
||||
});
|
||||
}
|
||||
|
||||
// 保证首次和 pjax 都能调用
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", fetchAndDisplayToots);
|
||||
} else {
|
||||
fetchAndDisplayToots();
|
||||
}
|
||||
document.addEventListener('pjax:end', fetchAndDisplayToots);
|
||||
</script>
|
||||
<style>
|
||||
div pre code {
|
||||
white-space: pre-wrap;
|
||||
@@ -159,8 +165,10 @@ img {
|
||||
height: 480px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
@@ -10,7 +10,11 @@
|
||||
</div>
|
||||
<div id="page-empty">
|
||||
<div id="page" class="row row-cols-1">
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<div id="post-main" class="col-lg-8 col-md-12 animated fadeInLeft ">
|
||||
<?php else: ?>
|
||||
<div id="post-main" class="col-lg-12 col-md-12">
|
||||
<?php endif; ?>
|
||||
<div class="p-block"><div>
|
||||
<h1 id="post-title" class="mb-0 puock-text t-xxl"><?php $this->title() ?></h1>
|
||||
</div>
|
||||
@@ -60,5 +64,7 @@ if($days > 180){
|
||||
<?php $this->need('comments.php'); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
<?php endif; ?>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
@@ -15,7 +15,11 @@
|
||||
<div class="puock-text p-block t-md ad-page-top"><?php $this->options->articletop(); ?></div>
|
||||
<?php endif; ?>
|
||||
<div class="row row-cols-1 post-row">
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<div id="post-main" class="col-lg-8 col-md-12 animated fadeInLeft ">
|
||||
<?php else: ?>
|
||||
<div id="post-main" class="col-lg-12 col-md-12">
|
||||
<?php endif; ?>
|
||||
<div class="p-block"><div>
|
||||
<h1 id="post-title" class="mb-0 puock-text t-xxl"><?php $this->title() ?></h1>
|
||||
</div>
|
||||
@@ -90,6 +94,7 @@ if($days > 180){
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分享海报点赞打赏 -->
|
||||
<div class="mt15 post-action-panel">
|
||||
<div class="post-action-content">
|
||||
<div class="d-flex justify-content-center w-100 c-sub">
|
||||
@@ -120,71 +125,102 @@ if($days > 180){
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!--内页中-->
|
||||
</div>
|
||||
<!--内页中-->
|
||||
</div>
|
||||
<?php if ($this->options->articlemid): ?>
|
||||
<div class="puock-text p-block t-md ad-page-content-bottom"><?php $this->options->articlemid(); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php $this->related(4)->to($relatedPosts); if ($relatedPosts->have()):?>
|
||||
<div class="p-block pb-0">
|
||||
<div class="row puock-text post-relevant">
|
||||
<?php while ($relatedPosts->next()): ?>
|
||||
<a href="<?php $relatedPosts->permalink(); ?>"
|
||||
class="col-6 col-md-3 post-relevant-item ww">
|
||||
<div
|
||||
style="background:url('<?php echo getPostCover($relatedPosts->content, $relatedPosts->cid); ?>')">
|
||||
<div class="title"> <?php $relatedPosts->title(); ?></div>
|
||||
</div>
|
||||
</a>
|
||||
<?php endwhile; ?>
|
||||
</div>
|
||||
<?php if ($this->options->articlemid): ?>
|
||||
<!--文章中广告-->
|
||||
<div class="puock-text p-block t-md ad-page-content-bottom"><?php $this->options->articlemid(); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php $this->related(4)->to($relatedPosts); if ($relatedPosts->have()):?>
|
||||
<!--相关文章-->
|
||||
<div class="p-block pb-0">
|
||||
<div class="row puock-text post-relevant">
|
||||
<?php while ($relatedPosts->next()): ?>
|
||||
<a href="<?php $relatedPosts->permalink(); ?>" class="col-6 col-md-3 post-relevant-item ww">
|
||||
<div style="background:url('<?php echo getPostCover($relatedPosts->content, $relatedPosts->cid); ?>')">
|
||||
<div class="title"> <?php $relatedPosts->title(); ?></div>
|
||||
</div>
|
||||
</a>
|
||||
<?php endwhile; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<!--上一篇与下一篇-->
|
||||
<div class="p-block p-lf-15">
|
||||
<div class="row text-center pd-links single-next-or-pre t-md">
|
||||
<div class="col-6 p-border-r-1 p-0">
|
||||
<?php
|
||||
// 查询上一篇(比当前文章更早的文章)
|
||||
$db = Typecho_Db::get();
|
||||
$prev = $db->fetchRow($db->select('cid', 'title', 'slug', 'created')
|
||||
->from('table.contents')
|
||||
->where('created < ?', $this->created) // 比当前文章更早
|
||||
->where('type = ?', 'post') // 只查询文章,排除页面
|
||||
->where('status = ?', 'publish') // 只查询已发布的
|
||||
->order('created', Typecho_Db::SORT_DESC) // 按时间降序(最近的上一篇)
|
||||
->limit(1));
|
||||
|
||||
if ($prev):
|
||||
// 生成正确链接(兼容伪静态和自定义固定链接)
|
||||
$prevUrl = Typecho_Router::url('post', $prev, $this->options->index);
|
||||
?>
|
||||
<a href="<?php echo $prevUrl; ?>" rel="prev" title="<?php echo $prev['title']; ?>">
|
||||
<div class="abhl puock-text">
|
||||
<p class="t-line-1"><?php echo $prev['title']; ?></p>
|
||||
<span>上一篇</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="p-block p-lf-15">
|
||||
<div class="row text-center pd-links single-next-or-pre t-md ">
|
||||
<?php $prevPost = get_previous_post($this); ?>
|
||||
<div class="col-6 p-border-r-1 p-0">
|
||||
<?php if ($prevPost) { ?>
|
||||
<a href="<?php echo $prevPost->permalink; ?>"
|
||||
rel="prev">
|
||||
<div class='abhl puock-text'>
|
||||
<p class='t-line-1'><?php echo $prevPost->title; ?></p>
|
||||
<span>上一篇</span>
|
||||
</div>
|
||||
</a>
|
||||
<?php } else { ?>
|
||||
<a href="javascript:void(0);" rel="prev">
|
||||
<div class='abhl puock-text'>
|
||||
<p class='t-line-1'>没有上一篇</p>
|
||||
<span>上一篇</span>
|
||||
</div>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php $nextPost = get_next_post($this); ?>
|
||||
<div class="col-6 p-0">
|
||||
<?php if ($nextPost) { ?>
|
||||
<a href="<?php echo $nextPost->permalink; ?>" rel="next">
|
||||
<div class="abhl">
|
||||
<p class="t-line-1"><?php echo $nextPost->title; ?></p>
|
||||
<span>下一篇</span>
|
||||
</div>
|
||||
</a>
|
||||
<?php } else { ?>
|
||||
<a href="javascript:void(0);" rel="next">
|
||||
<div class='abhl puock-text'>
|
||||
<p class='t-line-1'>已是最新的文章</p>
|
||||
<span>下一篇</span>
|
||||
</div>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!--评论上方-->
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a href="javascript:void(0);" rel="prev">
|
||||
<div class="abhl puock-text">
|
||||
<p class="t-line-1">没有上一篇</p>
|
||||
<span>上一篇</span>
|
||||
</div>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="col-6 p-0">
|
||||
<?php
|
||||
// 查询下一篇(比当前文章更新的文章)
|
||||
$next = $db->fetchRow($db->select('cid', 'title', 'slug', 'created')
|
||||
->from('table.contents')
|
||||
->where('created > ?', $this->created) // 比当前文章更新
|
||||
->where('type = ?', 'post') // 只查询文章,排除页面
|
||||
->where('status = ?', 'publish') // 只查询已发布的
|
||||
->order('created', Typecho_Db::SORT_ASC) // 按时间升序(最早的下一条)
|
||||
->limit(1));
|
||||
|
||||
if ($next):
|
||||
// 生成正确链接(兼容伪静态和自定义固定链接)
|
||||
$nextUrl = Typecho_Router::url('post', $next, $this->options->index);
|
||||
?>
|
||||
<a href="<?php echo $nextUrl; ?>" rel="next" title="<?php echo $next['title']; ?>">
|
||||
<div class="abhl puock-text">
|
||||
<p class="t-line-1"><?php echo $next['title']; ?></p>
|
||||
<span>下一篇</span>
|
||||
</div>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a href="javascript:void(0);" rel="next">
|
||||
<div class="abhl puock-text">
|
||||
<p class="t-line-1">已是最新文章</p>
|
||||
<span>下一篇</span>
|
||||
</div>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--评论上方-->
|
||||
<?php $this->need('comments.php'); ?>
|
||||
<?php if ($this->options->articlefoot): ?>
|
||||
<!--文章底部广告-->
|
||||
<div class="puock-text p-block t-md ad-comment-top"><?php $this->options->articlefoot(); ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
<?php endif; ?>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
+22
-48
@@ -18,57 +18,34 @@
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- 个人信息 -->
|
||||
<?php
|
||||
// 获取数据库连接
|
||||
$db = Typecho_Db::get();
|
||||
$prefix = $db->getPrefix();
|
||||
// 1. 获取uid=1的用户信息
|
||||
$user = $db->fetchRow($db->select()->from('table.users')->where('uid = ?', 1));
|
||||
$email = $user['mail'];
|
||||
$nickname = $user['screenName'];
|
||||
// 获取用户设置的 Gravatar 镜像
|
||||
$cnavatar = Helper::options()->cnavatar ? Helper::options()->cnavatar : 'https://cravatar.cn/avatar/';
|
||||
$hash = md5($email);
|
||||
$avatar = rtrim($cnavatar, '/') . '/' . $hash . '?s=80&d=identicon';
|
||||
// 2. 获取用户总数
|
||||
$userCount = $db->fetchObject($db->select(array('COUNT(*)' => 'num'))->from('table.users'))->num;
|
||||
// 3. 获取文章总数(只统计 type='post' 且 status='publish')
|
||||
$postCount = $db->fetchObject($db->select(array('COUNT(*)' => 'num'))->from('table.contents')->where('type = ?', 'post')->where('status = ?', 'publish'))->num;
|
||||
// 4. 获取评论总数
|
||||
$commentCount = $db->fetchObject($db->select(array('COUNT(*)' => 'num'))->from('table.comments'))->num;
|
||||
// 5. 获取文章浏览总量(累加所有文章的 views 字段)
|
||||
$totalViews = $db->fetchObject(
|
||||
$db->select(array('SUM(views)' => 'viewsum'))->from('table.contents')->where('type = ?', 'post')
|
||||
)->viewsum;
|
||||
if ($totalViews === null) $totalViews = 0;
|
||||
?>
|
||||
<!-- 个人信息 -->
|
||||
<?php $stats = get_site_statistics(); ?>
|
||||
<?php if (!empty($this->options->sidebarBlock) && in_array('ShowAdmin', $this->options->sidebarBlock)): ?>
|
||||
<div class="widget-puock-author widget">
|
||||
<div class="header" style="background-image: url('<?php echo $this->options->bgUrl() ?: $this->options->themeUrl('assets/img/cover.png'); ?>')">
|
||||
<img src='<?php $this->options->themeUrl('assets/img/load.svg'); ?>' class='lazy avatar' data-src='<?php echo $avatar; ?>' >
|
||||
<div class="header" style="background-image: url('<?php echo !empty($this->options->bgUrl) ? $this->options->bgUrl : $this->options->themeUrl('assets/img/cover.png'); ?>')">
|
||||
<img src='<?php $this->options->themeUrl('assets/img/load.svg'); ?>' class='lazy avatar' data-src='<?php echo $stats['avatar']; ?>' >
|
||||
</div>
|
||||
<div class="content t-md puock-text">
|
||||
<div class="text-center p-2">
|
||||
<div class="t-lg"><?php echo $nickname; ?></div>
|
||||
<div class="t-lg"><?php echo $stats['nickname']; ?></div>
|
||||
<div class="mt10 t-sm"><?php $this->options->description(); ?></div>
|
||||
</div>
|
||||
<div class="row mt10">
|
||||
<div class="col-3 text-center">
|
||||
<div class="c-sub t-sm">用户数</div>
|
||||
<div><?php echo $userCount; ?></div>
|
||||
<div><?php echo $stats['userCount']; ?></div>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<div class="c-sub t-sm">文章数</div>
|
||||
<div><?php echo $postCount; ?></div>
|
||||
<div><?php echo $stats['postCount']; ?></div>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<div class="c-sub t-sm">评论数</div>
|
||||
<div><?php echo $commentCount; ?></div>
|
||||
<div><?php echo $stats['commentCount']; ?></div>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<div class="c-sub t-sm">阅读量</div>
|
||||
<div><?php echo $totalViews; ?></div>
|
||||
<div><?php echo $stats['totalViews']; ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -132,27 +109,24 @@ if ($totalViews === null) $totalViews = 0;
|
||||
<div class="pk-widget p-block">
|
||||
<div>
|
||||
<span class="t-lg border-bottom border-primary puock-text pb-2">
|
||||
<i class="fa fa-chart-simple mr-1"></i>热门文章
|
||||
<i class="fa-solid fa-fire"></i> 热门文章
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt20">
|
||||
<?php foreach ($hotPosts as $post): ?>
|
||||
<?php
|
||||
// Typecho 1.3.0 兼容处理
|
||||
$widget = $this->widget('Widget_Archive@post_' . $post['cid'], 'type=post');
|
||||
// 更可靠的获取文章链接方式
|
||||
$widget = Typecho_Widget::widget('Widget_Contents_Post_Recent');
|
||||
$permalink = '';
|
||||
try {
|
||||
$widget->setArchiveProperty('cid', $post['cid']);
|
||||
$widget->setArchiveProperty('title', $post['title']);
|
||||
$widget->setArchiveProperty('slug', $post['slug']);
|
||||
$widget->setArchiveProperty('created', $post['created']);
|
||||
$widget->setArchiveProperty('authorId', $post['authorId']);
|
||||
$widget->setArchiveProperty('type', $post['type']);
|
||||
$widget->setArchiveProperty('status', $post['status']);
|
||||
$widget->setArchiveProperty('commentsNum', $post['commentsNum']);
|
||||
|
||||
// 生成正确链接
|
||||
$permalink = $widget->archiveUrl;
|
||||
|
||||
// 方法1:使用Typecho的Router类
|
||||
$permalink = Typecho_Router::url('post', $post, $this->options->index);
|
||||
// 方法2:或者使用辅助函数(如果方法1不行)
|
||||
if (empty($permalink)) {
|
||||
$widget->push($post);
|
||||
$permalink = $widget->permalink;
|
||||
$widget->pop();
|
||||
}
|
||||
if (empty($post['title']) || empty($permalink)) {
|
||||
continue;
|
||||
}
|
||||
@@ -177,7 +151,7 @@ if ($totalViews === null) $totalViews = 0;
|
||||
<?php endif; ?>
|
||||
<!-- 最近评论 -->
|
||||
<?php if (!empty($this->options->sidebarBlock) && in_array('ShowRecentComments', $this->options->sidebarBlock)): ?>
|
||||
<?php
|
||||
<?php
|
||||
// 设置参数来排除管理员评论
|
||||
$comments = \Widget\Comments\Recent::alloc(array(
|
||||
'ignoreAuthor' => true // 这里添加参数来排除作者/管理员评论
|
||||
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit7f036e4555c1f5c49b96f869c5b4f1bf::getLoader();
|
||||
Vendored
+579
@@ -0,0 +1,579 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @var string|null */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array<string, list<string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
/**
|
||||
* List of PSR-0 prefixes
|
||||
*
|
||||
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||
*
|
||||
* @var array<string, array<string, list<string>>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var string|null */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var array<string, self>
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param string|null $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string> Array of classname => path
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $classMap Class to filename map
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 base directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
$includeFile = self::$includeFile;
|
||||
$includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||
*
|
||||
* @return array<string, self>
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function initializeIncludeClosure()
|
||||
{
|
||||
if (self::$includeFile !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
self::$includeFile = \Closure::bind(static function($file) {
|
||||
include $file;
|
||||
}, null, null);
|
||||
}
|
||||
}
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagesByType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints((string) $constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require $vendorDir.'/composer/installed.php';
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = $required;
|
||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||
self::$installed = $installed[count($installed) - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require __DIR__ . '/installed.php';
|
||||
self::$installed = $required;
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (self::$installed !== array()) {
|
||||
$installed[] = self::$installed;
|
||||
}
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
);
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'dc1275c308c5b416beb314b6317daca2' => $vendorDir . '/overtrue/pinyin/src/const.php',
|
||||
);
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Overtrue\\Pinyin\\' => array($vendorDir . '/overtrue/pinyin/src'),
|
||||
);
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit7f036e4555c1f5c49b96f869c5b4f1bf
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit7f036e4555c1f5c49b96f869c5b4f1bf', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit7f036e4555c1f5c49b96f869c5b4f1bf', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit7f036e4555c1f5c49b96f869c5b4f1bf::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInit7f036e4555c1f5c49b96f869c5b4f1bf::$files;
|
||||
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
}, null, null);
|
||||
foreach ($filesToLoad as $fileIdentifier => $file) {
|
||||
$requireFile($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit7f036e4555c1f5c49b96f869c5b4f1bf
|
||||
{
|
||||
public static $files = array (
|
||||
'dc1275c308c5b416beb314b6317daca2' => __DIR__ . '/..' . '/overtrue/pinyin/src/const.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'O' =>
|
||||
array (
|
||||
'Overtrue\\Pinyin\\' => 16,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Overtrue\\Pinyin\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/overtrue/pinyin/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit7f036e4555c1f5c49b96f869c5b4f1bf::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit7f036e4555c1f5c49b96f869c5b4f1bf::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInit7f036e4555c1f5c49b96f869c5b4f1bf::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"name": "overtrue/pinyin",
|
||||
"version": "4.1.0",
|
||||
"version_normalized": "4.1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/overtrue/pinyin.git",
|
||||
"reference": "4d0fb4f27f0c79e81c9489e0c0ae4a4f8837eae7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/overtrue/pinyin/zipball/4d0fb4f27f0c79e81c9489e0c0ae4a4f8837eae7",
|
||||
"reference": "4d0fb4f27f0c79e81c9489e0c0ae4a4f8837eae7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"brainmaestro/composer-git-hooks": "^2.7",
|
||||
"friendsofphp/php-cs-fixer": "^2.16",
|
||||
"phpunit/phpunit": "~8.0"
|
||||
},
|
||||
"time": "2023-04-27T10:17:12+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"hooks": {
|
||||
"pre-push": [
|
||||
"composer test",
|
||||
"composer check-style"
|
||||
],
|
||||
"pre-commit": [
|
||||
"composer test",
|
||||
"composer fix-style"
|
||||
]
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/const.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Overtrue\\Pinyin\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "overtrue",
|
||||
"email": "anzhengchao@gmail.com",
|
||||
"homepage": "http://github.com/overtrue"
|
||||
}
|
||||
],
|
||||
"description": "Chinese to pinyin translator.",
|
||||
"homepage": "https://github.com/overtrue/pinyin",
|
||||
"keywords": [
|
||||
"Chinese",
|
||||
"Pinyin",
|
||||
"cn2pinyin"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/overtrue/pinyin/issues",
|
||||
"source": "https://github.com/overtrue/pinyin/tree/4.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/overtrue",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"install-path": "../overtrue/pinyin"
|
||||
}
|
||||
],
|
||||
"dev": true,
|
||||
"dev-package-names": []
|
||||
}
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => '__root__',
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'overtrue/pinyin' => array(
|
||||
'pretty_version' => '4.1.0',
|
||||
'version' => '4.1.0.0',
|
||||
'reference' => '4d0fb4f27f0c79e81c9489e0c0ae4a4f8837eae7',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../overtrue/pinyin',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
),
|
||||
);
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 70100)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.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;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: overtrue
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
custom: # Replace with a single custom sponsorship URL
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: composer
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "21:00"
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: phpunit/phpunit
|
||||
versions:
|
||||
- ">= 8.a, < 9"
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 安正超
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Vendored
+130
@@ -0,0 +1,130 @@
|
||||
<h1 align="center">Pinyin</h1>
|
||||
|
||||
<p align="center">
|
||||
|
||||
[](https://travis-ci.org/overtrue/pinyin)
|
||||
[](https://packagist.org/packages/overtrue/pinyin) [](https://packagist.org/packages/overtrue/pinyin) [](https://packagist.org/packages/overtrue/pinyin) [](https://packagist.org/packages/overtrue/pinyin)
|
||||
[](https://scrutinizer-ci.com/g/overtrue/pinyin/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/overtrue/pinyin/?branch=master)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fovertrue%2Fpinyin?ref=badge_shield)
|
||||
|
||||
</p>
|
||||
|
||||
:cn: 基于 [CC-CEDICT](http://cc-cedict.org/wiki/) 词典的中文转拼音工具,更准确的支持多音字的汉字转拼音解决方案。
|
||||
|
||||
|
||||
## 安装
|
||||
|
||||
使用 Composer 安装:
|
||||
|
||||
```
|
||||
$ composer require "overtrue/pinyin:~4.0"
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
可选转换方案:
|
||||
|
||||
- 内存型,适用于服务器内存空间较富余,优点:转换快
|
||||
- 小内存型(默认),适用于内存比较紧张的环境,优点:占用内存小,转换不如内存型快
|
||||
- I/O型,适用于虚拟机,内存限制比较严格环境。优点:非常微小内存消耗。缺点:转换慢,不如内存型转换快,php >= 5.5
|
||||
|
||||
## 可用选项:
|
||||
|
||||
| 选项 | 描述 |
|
||||
| ------------- | ---------------------------------------------------|
|
||||
| `PINYIN_TONE` | UNICODE 式音调:`měi hǎo` |
|
||||
| `PINYIN_ASCII_TONE` | 带数字式音调: `mei3 hao3` |
|
||||
| `PINYIN_NO_TONE` | 无音调:`mei hao` |
|
||||
| `PINYIN_KEEP_NUMBER` | 保留数字 |
|
||||
| `PINYIN_KEEP_ENGLISH` | 保留英文 |
|
||||
| `PINYIN_KEEP_PUNCTUATION` | 保留标点 |
|
||||
| `PINYIN_UMLAUT_V` | 使用 `v` 代替 `yu`, 例如:吕 `lyu` 将会转为 `lv` |
|
||||
|
||||
### 拼音数组
|
||||
|
||||
```php
|
||||
use Overtrue\Pinyin\Pinyin;
|
||||
|
||||
// 小内存型
|
||||
$pinyin = new Pinyin(); // 默认
|
||||
// 内存型
|
||||
// $pinyin = new Pinyin('\\Overtrue\\Pinyin\\MemoryFileDictLoader');
|
||||
// I/O型
|
||||
// $pinyin = new Pinyin('\\Overtrue\\Pinyin\\GeneratorFileDictLoader');
|
||||
|
||||
$pinyin->convert('带着希望去旅行,比到达终点更美好');
|
||||
// ["dai", "zhe", "xi", "wang", "qu", "lyu", "xing", "bi", "dao", "da", "zhong", "dian", "geng", "mei", "hao"]
|
||||
|
||||
$pinyin->convert('带着希望去旅行,比到达终点更美好', PINYIN_TONE);
|
||||
// ["dài","zhe","xī","wàng","qù","lǚ","xíng","bǐ","dào","dá","zhōng","diǎn","gèng","měi","hǎo"]
|
||||
|
||||
$pinyin->convert('带着希望去旅行,比到达终点更美好', PINYIN_ASCII_TONE);
|
||||
//["dai4","zhe","xi1","wang4","qu4","lyu3","xing2","bi3","dao4","da2","zhong1","dian3","geng4","mei3","hao3"]
|
||||
```
|
||||
|
||||
- 小内存型: 将字典分片载入内存
|
||||
- 内存型: 将所有字典预先载入内存
|
||||
- I/O型: 不载入内存,将字典使用文件流打开逐行遍历并运用php5.5生成器(yield)特性分配单行内存
|
||||
|
||||
|
||||
### 生成用于链接的拼音字符串
|
||||
|
||||
```php
|
||||
$pinyin->permalink('带着希望去旅行'); // dai-zhe-xi-wang-qu-lyu-xing
|
||||
$pinyin->permalink('带着希望去旅行', '.'); // dai.zhe.xi.wang.qu.lyu.xing
|
||||
```
|
||||
|
||||
### 获取首字符字符串
|
||||
|
||||
```php
|
||||
$pinyin->abbr('带着希望去旅行'); // dzxwqlx
|
||||
$pinyin->abbr('带着希望去旅行', '-'); // d-z-x-w-q-l-x
|
||||
|
||||
$pinyin->abbr('你好2018!', PINYIN_KEEP_NUMBER); // nh2018
|
||||
$pinyin->abbr('Happy New Year! 2018!', PINYIN_KEEP_ENGLISH); // HNY2018
|
||||
```
|
||||
|
||||
### 翻译整段文字为拼音
|
||||
|
||||
将会保留中文字符:`,。 ! ? : “ ” ‘ ’` 并替换为对应的英文符号。
|
||||
|
||||
```php
|
||||
$pinyin->sentence('带着希望去旅行,比到达终点更美好!');
|
||||
// dai zhe xi wang qu lyu xing, bi dao da zhong dian geng mei hao!
|
||||
|
||||
$pinyin->sentence('带着希望去旅行,比到达终点更美好!', PINYIN_TONE);
|
||||
// dài zhe xī wàng qù lǚ xíng, bǐ dào dá zhōng diǎn gèng měi hǎo!
|
||||
```
|
||||
|
||||
### 翻译姓名
|
||||
|
||||
姓名的姓的读音有些与普通字不一样,比如 ‘单’ 常见的音为 `dan`,而作为姓的时候读 `shan`。
|
||||
|
||||
```php
|
||||
$pinyin->name('单某某'); // ['shan', 'mou', 'mou']
|
||||
$pinyin->name('单某某', PINYIN_TONE); // ["shàn","mǒu","mǒu"]
|
||||
```
|
||||
|
||||
更多使用请参考 [测试用例](https://github.com/overtrue/pinyin/blob/master/tests/AbstractDictLoaderTestCase.php)。
|
||||
|
||||
## 在 Laravel 中使用
|
||||
|
||||
独立的包在这里:[overtrue/laravel-pinyin](https://github.com/overtrue/laravel-pinyin)
|
||||
|
||||
## Contribution
|
||||
欢迎提意见及完善补充词库 [`overtrue/pinyin-dictionary-maker`](https://github.com/overtrue/pinyin-dictionary-maker/tree/master/patches) :kiss:
|
||||
|
||||
## 参考
|
||||
|
||||
- [详细参考资料](https://github.com/overtrue/pinyin-resources)
|
||||
|
||||
## PHP 扩展包开发
|
||||
|
||||
> 想知道如何从零开始构建 PHP 扩展包?
|
||||
>
|
||||
> 请关注我的实战课程,我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package)
|
||||
|
||||
# License
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fovertrue%2Fpinyin?ref=badge_large)
|
||||
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"name": "overtrue/pinyin",
|
||||
"description": "Chinese to pinyin translator.",
|
||||
"keywords": [
|
||||
"chinese",
|
||||
"pinyin",
|
||||
"cn2pinyin"
|
||||
],
|
||||
"homepage": "https://github.com/overtrue/pinyin",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "overtrue",
|
||||
"homepage": "http://github.com/overtrue",
|
||||
"email": "anzhengchao@gmail.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Overtrue\\Pinyin\\": "src/"
|
||||
},
|
||||
"files": ["src/const.php"]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Overtrue\\Pinyin\\Test\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php":">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~8.0",
|
||||
"brainmaestro/composer-git-hooks": "^2.7",
|
||||
"friendsofphp/php-cs-fixer": "^2.16"
|
||||
},
|
||||
"extra": {
|
||||
"hooks": {
|
||||
"pre-commit": [
|
||||
"composer test",
|
||||
"composer fix-style"
|
||||
],
|
||||
"pre-push": [
|
||||
"composer test",
|
||||
"composer check-style"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-update-cmd": [
|
||||
"cghooks update"
|
||||
],
|
||||
"post-merge": "composer install",
|
||||
"post-install-cmd": [
|
||||
"cghooks add --ignore-lock",
|
||||
"cghooks update"
|
||||
],
|
||||
"cghooks": "vendor/bin/cghooks",
|
||||
"check-style": "php-cs-fixer fix --using-cache=no --diff --config=.php_cs --dry-run --ansi",
|
||||
"fix-style": "php-cs-fixer fix --using-cache=no --config=.php_cs --ansi",
|
||||
"test": "vendor/bin/phpunit --colors=always"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"test": "Run all tests.",
|
||||
"check-style": "Run style checks (only dry run - no fixing!).",
|
||||
"fix-style": "Run style checks and fix violations."
|
||||
}
|
||||
}
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
return array (
|
||||
'万俟' => ' mò qí',
|
||||
'尉迟' => ' yù chí',
|
||||
'单于' => ' chán yú',
|
||||
'不' => ' fǒu',
|
||||
'沈' => ' shěn',
|
||||
'称' => ' chēng',
|
||||
'车' => ' chē',
|
||||
'万' => ' wàn',
|
||||
'汤' => ' tāng',
|
||||
'阿' => ' ā',
|
||||
'丁' => ' dīng',
|
||||
'强' => ' qiáng',
|
||||
'仇' => ' qiú',
|
||||
'叶' => ' yè',
|
||||
'阚' => ' kàn',
|
||||
'乐' => ' yuè',
|
||||
'乜' => ' niè',
|
||||
'陆' => ' lù',
|
||||
'殷' => ' yīn',
|
||||
'牟' => ' móu',
|
||||
'区' => ' ōu',
|
||||
'宿' => ' sù',
|
||||
'俞' => ' yú',
|
||||
'余' => ' yú',
|
||||
'齐' => ' qí',
|
||||
'许' => ' xǔ',
|
||||
'信' => ' xìn',
|
||||
'无' => ' wú',
|
||||
'浣' => ' wǎn',
|
||||
'艾' => ' ài',
|
||||
'浅' => ' qiǎn',
|
||||
'烟' => ' yān',
|
||||
'蓝' => ' lán',
|
||||
'於' => ' yú',
|
||||
'寻' => ' xún',
|
||||
'殳' => ' shū',
|
||||
'思' => ' sī',
|
||||
'鸟' => ' niǎo',
|
||||
'卜' => ' bǔ',
|
||||
'单' => ' shàn',
|
||||
'南' => ' nán',
|
||||
'柏' => ' bǎi',
|
||||
'朴' => ' piáo',
|
||||
'繁' => ' pó',
|
||||
'曾' => ' zēng',
|
||||
'瞿' => ' qú',
|
||||
'缪' => ' miào',
|
||||
'石' => ' shí',
|
||||
'冯' => ' féng',
|
||||
'覃' => ' qín',
|
||||
'幺' => ' yāo',
|
||||
'种' => ' chóng',
|
||||
'折' => ' shè',
|
||||
'燕' => ' yān',
|
||||
'纪' => ' jǐ',
|
||||
'过' => ' guō',
|
||||
'华' => ' huà',
|
||||
'冼' => ' xiǎn',
|
||||
'秘' => ' bì',
|
||||
'重' => ' chóng',
|
||||
'解' => ' xiè',
|
||||
'那' => ' nā',
|
||||
'和' => ' hé',
|
||||
'贾' => ' jiǎ',
|
||||
'塔' => ' tǎ',
|
||||
'盛' => ' shèng',
|
||||
'查' => ' zhā',
|
||||
'盖' => ' gě',
|
||||
'居' => ' jū',
|
||||
'哈' => ' hǎ',
|
||||
'的' => ' dē',
|
||||
'薄' => ' bó',
|
||||
'佴' => ' nài',
|
||||
'六' => ' lù',
|
||||
'都' => ' dū',
|
||||
'翟' => ' zhái',
|
||||
'扎' => ' zā',
|
||||
'藏' => ' zàng',
|
||||
'粘' => ' niàn',
|
||||
'难' => ' nàn',
|
||||
'若' => ' ruò',
|
||||
'貟' => ' yùn',
|
||||
'贠' => ' yùn',
|
||||
);
|
||||
Vendored
+8003
File diff suppressed because it is too large
Load Diff
Vendored
+8003
File diff suppressed because it is too large
Load Diff
Vendored
+8003
File diff suppressed because it is too large
Load Diff
Vendored
+8003
File diff suppressed because it is too large
Load Diff
Vendored
+8003
File diff suppressed because it is too large
Load Diff
Vendored
+2055
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Dict loader interface.
|
||||
*/
|
||||
interface DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* <pre>
|
||||
* [
|
||||
* '响应时间' => "[\t]xiǎng[\t]yìng[\t]shí[\t]jiān",
|
||||
* '长篇连载' => '[\t]cháng[\t]piān[\t]lián[\t]zǎi',
|
||||
* //...
|
||||
* ]
|
||||
* </pre>
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback);
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback);
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
|
||||
class FileDictLoader implements DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Words segment name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $segmentName = 'words_%s';
|
||||
|
||||
/**
|
||||
* Dict path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback)
|
||||
{
|
||||
for ($i = 0; $i < 100; ++$i) {
|
||||
$segment = $this->path . '/' . sprintf($this->segmentName, $i);
|
||||
|
||||
if (file_exists($segment)) {
|
||||
$dictionary = (array) include $segment;
|
||||
$callback($dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback)
|
||||
{
|
||||
$surnames = $this->path . '/surnames';
|
||||
|
||||
if (file_exists($surnames)) {
|
||||
$dictionary = (array) include $surnames;
|
||||
$callback($dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
use SplFileObject;
|
||||
use Generator;
|
||||
|
||||
class GeneratorFileDictLoader implements DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Data directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Words segment name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $segmentName = 'words_%s';
|
||||
|
||||
/**
|
||||
* SplFileObjects.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $handles = [];
|
||||
|
||||
/**
|
||||
* surnames.
|
||||
*
|
||||
* @var SplFileObject
|
||||
*/
|
||||
protected static $surnamesHandle;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
for ($i = 0; $i < 100; ++$i) {
|
||||
$segment = $this->path . '/' . sprintf($this->segmentName, $i);
|
||||
|
||||
if (file_exists($segment) && is_file($segment)) {
|
||||
array_push(static::$handles, $this->openFile($segment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new file object.
|
||||
*
|
||||
* @param string $filename file path
|
||||
* @param string $mode file open mode
|
||||
*
|
||||
* @return SplFileObject
|
||||
*/
|
||||
protected function openFile($filename, $mode = 'r')
|
||||
{
|
||||
return new SplFileObject($filename, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* get Generator syntax.
|
||||
*
|
||||
* @param array $handles SplFileObjects
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
protected function getGenerator(array $handles)
|
||||
{
|
||||
foreach ($handles as $handle) {
|
||||
$handle->seek(0);
|
||||
while (false === $handle->eof()) {
|
||||
$string = str_replace(['\'', ' ', PHP_EOL, ','], '', $handle->fgets());
|
||||
|
||||
if (false === strpos($string, '=>')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($string, $pinyin) = explode('=>', $string);
|
||||
|
||||
yield $string => $pinyin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the stream.
|
||||
*
|
||||
* @param Generator $generator
|
||||
* @param Closure $callback
|
||||
*
|
||||
* @author Seven Du <shiweidu@outlook.com>
|
||||
*/
|
||||
protected function traversing(Generator $generator, Closure $callback)
|
||||
{
|
||||
foreach ($generator as $string => $pinyin) {
|
||||
$callback([$string => $pinyin]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback)
|
||||
{
|
||||
$this->traversing($this->getGenerator(static::$handles), $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback)
|
||||
{
|
||||
if (!static::$surnamesHandle instanceof SplFileObject) {
|
||||
static::$surnamesHandle = $this->openFile($this->path . '/surnames');
|
||||
}
|
||||
|
||||
$this->traversing($this->getGenerator([static::$surnamesHandle]), $callback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
|
||||
class MemoryFileDictLoader implements DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Data directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Words segment name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $segmentName = 'words_%s';
|
||||
|
||||
/**
|
||||
* Segment files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $segments = [];
|
||||
|
||||
/**
|
||||
* Surname cache.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $surnames = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
for ($i = 0; $i < 100; ++$i) {
|
||||
$segment = $path . '/' . sprintf($this->segmentName, $i);
|
||||
|
||||
if (file_exists($segment)) {
|
||||
$this->segments[] = (array) include $segment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback)
|
||||
{
|
||||
foreach ($this->segments as $dictionary) {
|
||||
$callback($dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback)
|
||||
{
|
||||
if (empty($this->surnames)) {
|
||||
$surnames = $this->path . '/surnames';
|
||||
|
||||
if (file_exists($surnames)) {
|
||||
$this->surnames = (array) include $surnames;
|
||||
}
|
||||
}
|
||||
|
||||
$callback($this->surnames);
|
||||
}
|
||||
}
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Pinyin
|
||||
{
|
||||
/**
|
||||
* Dict loader.
|
||||
*
|
||||
* @var \Overtrue\Pinyin\DictLoaderInterface
|
||||
*/
|
||||
protected $loader;
|
||||
|
||||
/**
|
||||
* Punctuations map.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $punctuations = [
|
||||
',' => ',',
|
||||
'。' => '.',
|
||||
'!' => '!',
|
||||
'?' => '?',
|
||||
':' => ':',
|
||||
'“' => '"',
|
||||
'”' => '"',
|
||||
'‘' => "'",
|
||||
'’' => "'",
|
||||
'_' => '_',
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $loaderName
|
||||
*/
|
||||
public function __construct($loaderName = null)
|
||||
{
|
||||
$this->loader = $loaderName ?: 'Overtrue\\Pinyin\\FileDictLoader';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $option
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function convert($string, $option = PINYIN_DEFAULT)
|
||||
{
|
||||
$pinyin = $this->romanize($string, $option);
|
||||
|
||||
return $this->splitWords($pinyin, $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string (person name) to pinyin.
|
||||
*
|
||||
* @param string $stringName
|
||||
* @param int $option
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function name($stringName, $option = PINYIN_NAME)
|
||||
{
|
||||
$option = $option | PINYIN_NAME;
|
||||
|
||||
$pinyin = $this->romanize($stringName, $option);
|
||||
|
||||
return $this->splitWords($pinyin, $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pinyin permalink from string.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function permalink($string, $delimiter = '-', $option = PINYIN_DEFAULT)
|
||||
{
|
||||
if (\is_int($delimiter)) {
|
||||
list($option, $delimiter) = [$delimiter, '-'];
|
||||
}
|
||||
|
||||
if (!in_array($delimiter, ['_', '-', '.', ''], true)) {
|
||||
throw new InvalidArgumentException("Delimiter must be one of: '_', '-', '', '.'.");
|
||||
}
|
||||
|
||||
return implode($delimiter, $this->convert($string, $option | \PINYIN_KEEP_NUMBER | \PINYIN_KEEP_ENGLISH));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first letters.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function abbr($string, $delimiter = '', $option = PINYIN_DEFAULT)
|
||||
{
|
||||
if (\is_int($delimiter)) {
|
||||
list($option, $delimiter) = [$delimiter, ''];
|
||||
}
|
||||
|
||||
return implode($delimiter, array_map(function ($pinyin) {
|
||||
return \is_numeric($pinyin) || preg_match('/\d+/', $pinyin) ? $pinyin : mb_substr($pinyin, 0, 1);
|
||||
}, $this->convert($string, $option | PINYIN_NO_TONE)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chinese phrase to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function phrase($string, $delimiter = ' ', $option = PINYIN_DEFAULT)
|
||||
{
|
||||
if (\is_int($delimiter)) {
|
||||
list($option, $delimiter) = [$delimiter, ' '];
|
||||
}
|
||||
|
||||
return implode($delimiter, $this->convert($string, $option));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chinese to pinyin sentence.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function sentence($string, $delimiter = ' ', $option = \PINYIN_NO_TONE)
|
||||
{
|
||||
if (\is_int($delimiter)) {
|
||||
list($option, $delimiter) = [$delimiter, ' '];
|
||||
}
|
||||
|
||||
return implode($delimiter, $this->convert($string, $option | \PINYIN_KEEP_PUNCTUATION | \PINYIN_KEEP_ENGLISH | \PINYIN_KEEP_NUMBER));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader setter.
|
||||
*
|
||||
* @param \Overtrue\Pinyin\DictLoaderInterface $loader
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLoader(DictLoaderInterface $loader)
|
||||
{
|
||||
$this->loader = $loader;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return dict loader,.
|
||||
*
|
||||
* @return \Overtrue\Pinyin\DictLoaderInterface
|
||||
*/
|
||||
public function getLoader()
|
||||
{
|
||||
if (!($this->loader instanceof DictLoaderInterface)) {
|
||||
$dataDir = dirname(__DIR__) . '/data/';
|
||||
|
||||
$loaderName = $this->loader;
|
||||
$this->loader = new $loaderName($dataDir);
|
||||
}
|
||||
|
||||
return $this->loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Chinese to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function romanize($string, $option = \PINYIN_DEFAULT)
|
||||
{
|
||||
$string = $this->prepare($string, $option);
|
||||
|
||||
$dictLoader = $this->getLoader();
|
||||
|
||||
if ($this->hasOption($option, \PINYIN_NAME)) {
|
||||
$string = $this->convertSurname($string, $dictLoader);
|
||||
}
|
||||
|
||||
$dictLoader->map(function ($dictionary) use (&$string) {
|
||||
$string = strtr($string, $dictionary);
|
||||
});
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Chinese Surname to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param \Overtrue\Pinyin\DictLoaderInterface $dictLoader
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function convertSurname($string, $dictLoader)
|
||||
{
|
||||
$dictLoader->mapSurname(function ($dictionary) use (&$string) {
|
||||
foreach ($dictionary as $surname => $pinyin) {
|
||||
if (0 === strpos($string, $surname)) {
|
||||
$string = $pinyin . mb_substr($string, mb_strlen($surname, 'UTF-8'), mb_strlen($string, 'UTF-8') - 1, 'UTF-8');
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split pinyin string to words.
|
||||
*
|
||||
* @param string $pinyin
|
||||
* @param string $option
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function splitWords($pinyin, $option)
|
||||
{
|
||||
$split = array_filter(preg_split('/\s+/i', $pinyin));
|
||||
|
||||
if (!$this->hasOption($option, PINYIN_TONE)) {
|
||||
foreach ($split as $index => $pinyin) {
|
||||
$split[$index] = $this->formatTone($pinyin, $option);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($split);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $option
|
||||
* @param int $check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOption($option, $check)
|
||||
{
|
||||
return ($option & $check) === $check;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-process.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prepare($string, $option = \PINYIN_DEFAULT)
|
||||
{
|
||||
$string = preg_replace_callback('~[a-z0-9_-]+~i', function ($matches) {
|
||||
return "\t" . $matches[0];
|
||||
}, $string);
|
||||
|
||||
$regex = ['\x{3007}\x{2E80}-\x{2FFF}\x{3100}-\x{312F}\x{31A0}-\x{31EF}\x{3400}-\x{4DBF}\x{4E00}-\x{9FFF}\x{F900}-\x{FAFF}', '\p{Z}', '\p{M}', "\t"];
|
||||
|
||||
if ($this->hasOption($option, \PINYIN_KEEP_NUMBER)) {
|
||||
\array_push($regex, '0-9');
|
||||
}
|
||||
|
||||
if ($this->hasOption($option, \PINYIN_KEEP_ENGLISH)) {
|
||||
\array_push($regex, 'a-zA-Z');
|
||||
}
|
||||
|
||||
if ($this->hasOption($option, \PINYIN_KEEP_PUNCTUATION)) {
|
||||
$punctuations = array_merge($this->punctuations, ["\t" => ' ', ' ' => ' ']);
|
||||
$string = trim(str_replace(array_keys($punctuations), $punctuations, $string));
|
||||
|
||||
\array_push($regex, preg_quote(implode(array_merge(array_keys($this->punctuations), $this->punctuations)), '~'));
|
||||
}
|
||||
|
||||
return preg_replace(\sprintf('~[^%s]~u', implode($regex)), '', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format.
|
||||
*
|
||||
* @param string $pinyin
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function formatTone($pinyin, $option = \PINYIN_NO_TONE)
|
||||
{
|
||||
$replacements = [
|
||||
// mb_chr(593) => 'ɑ' 轻声中除了 `ɑ` 和 `ü` 以外,其它和字母一样
|
||||
'ɑ' => ['a', 5], 'ü' => ['yu', 5],
|
||||
'üē' => ['ue', 1], 'üé' => ['ue', 2], 'üě' => ['ue', 3], 'üè' => ['ue', 4],
|
||||
'ā' => ['a', 1], 'ē' => ['e', 1], 'ī' => ['i', 1], 'ō' => ['o', 1], 'ū' => ['u', 1], 'ǖ' => ['yu', 1],
|
||||
'á' => ['a', 2], 'é' => ['e', 2], 'í' => ['i', 2], 'ó' => ['o', 2], 'ú' => ['u', 2], 'ǘ' => ['yu', 2],
|
||||
'ǎ' => ['a', 3], 'ě' => ['e', 3], 'ǐ' => ['i', 3], 'ǒ' => ['o', 3], 'ǔ' => ['u', 3], 'ǚ' => ['yu', 3],
|
||||
'à' => ['a', 4], 'è' => ['e', 4], 'ì' => ['i', 4], 'ò' => ['o', 4], 'ù' => ['u', 4], 'ǜ' => ['yu', 4],
|
||||
];
|
||||
|
||||
foreach ($replacements as $unicode => $replacement) {
|
||||
if (false !== strpos($pinyin, $unicode)) {
|
||||
$umlaut = $replacement[0];
|
||||
|
||||
// https://zh.wikipedia.org/wiki/%C3%9C
|
||||
if ($this->hasOption($option, \PINYIN_UMLAUT_V) && 'yu' == $umlaut) {
|
||||
$umlaut = 'v';
|
||||
}
|
||||
|
||||
$pinyin = str_replace($unicode, $umlaut, $pinyin) . ($this->hasOption($option, PINYIN_ASCII_TONE) ? $replacement[1] : '');
|
||||
}
|
||||
}
|
||||
|
||||
return $pinyin;
|
||||
}
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
define('PINYIN_DEFAULT', 4096);
|
||||
define('PINYIN_TONE', 2);
|
||||
define('PINYIN_NO_TONE', 4);
|
||||
define('PINYIN_ASCII_TONE', 8);
|
||||
define('PINYIN_NAME', 16);
|
||||
define('PINYIN_KEEP_NUMBER', 32);
|
||||
define('PINYIN_KEEP_ENGLISH', 64);
|
||||
define('PINYIN_UMLAUT_V', 128);
|
||||
define('PINYIN_KEEP_PUNCTUATION', 256);
|
||||
Reference in New Issue
Block a user