3.2.36 新增「自动头像」:按邮箱本地生成,14 套 CC0/MIT 风格可选 #5
为没有头像的访客按邮箱确定性地生成一张 SVG 头像,替代 Gravatar 那个灰底"神秘人"(cn.gravatar.com 在国内已超时,不勾加速时头像 干脆是空白)。全程纯 PHP,零外网请求、零落盘。 后台「主题设置」新增「自动头像」复选框 + 风格列表:勾上之后才 出现 14 套风格,点一下即时换 16 格预览,保存后全站生效。 实现要点(都是踩出来的): - PRNG 必须与 DiceBear 9.x 逐位一致:xorshift + hashSeed 全程跑在 JS 的 int32 语义里,PHP 64 位整数要逐步 i32() 归一; charCodeAt 按 UTF-16 码元取值,emoji 必须真做代理对拆分。 - pickComponent 有简繁两版:只有 glass/rings/shapes/thumbs 额外取 三次整数(rotation/offsetX/offsetY)。给简版也补三次 ⇒ PRNG 多走 3N 步 ⇒ 31 套里 27 套全错。编译期算 pc_has_ints 才治得住。 - 自带人类调色板:官方默认配色几乎全单值,30 个邮箱只出 2 种配色, 那才是"默认头像丑"的真因,光换画风解决不了。 - 只收录 CC0/MIT(13 CC0 + 1 MIT),CC BY 那 13 套要求署名,一律不收—— 主题装给用户用,用户不会维护署名,收进来等于转嫁合规义务。 接入 get_avatar 的两个关键修正: - 必须先看原 src 是不是 Gravatar 家族域名再决定是否替换。原先无条件 替换会把媒体库头像、外链头像、别的插件处理过的 data: 头像一起盖掉。 - 必须删 srcset。只改 src 留着 srcset,浏览器优先用 srcset, 生成的图根本不会被加载(现象是"代码明明换了,页面还是老样子"), 且不能重建 <img> 标签(会丢掉评论头像 CSS 依赖的 class)。 后台三处必须这么做: - body class 由 PHP 输出(admin_body_class),否则每次进设置页/保存后 重载都会先显示风格列表再藏起来,闪一下。 - 事件监听 radio 的 change,不能监听 label 的 click —— label 会把 click 转发给关联 input,冒泡回来再触发一次 ⇒ 同一风格发两次请求 (实测 hits 2→4)。 - 首屏已渲染的 16 格写进 JS 缓存,否则点一圈回到默认风格会白请求一次。 不依赖任何可选扩展:不用 mbstring(未安装时 mb_substr 是 Fatal error, 整站白屏)、不用 gd/imagick。UTF-8 切分/截断/大写全部自己实现。 PHP 7.0 语法,PHP 8.5 下 E_ALL 无任何 warning。 验证: - 14 套 × 6 种子 = 84 个用例与 DiceBear 官方 Node 输出逐字节一致, PHP 8.4 / 8.5 / 无 mbstring(php -n)三种环境均 84/0。 - 静态守卫 55/0;反向验证 17 项注入全部被捕获。 - 后台面板浏览器验证 26/0(显隐、几何、点选、缓存、连点、零报错)。 - 包内内容契约 24/0;包校验反向验证 6 项注入全部被捕获。 - 单次渲染稳态 0.047~0.105ms,峰值内存 2~4MB。
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
return array(
|
||||
'name' => 'rings',
|
||||
'meta' => array(
|
||||
'title' => 'Rings',
|
||||
'creator' => 'DiceBear',
|
||||
'source' => 'https://www.dicebear.com',
|
||||
'license_name' => 'CC0 1.0',
|
||||
'license_url' => 'https://creativecommons.org/publicdomain/zero/1.0/',
|
||||
),
|
||||
'view_box' => '0 0 100 100',
|
||||
'clip_default' => true,
|
||||
'has_pre_create' => false,
|
||||
'has_post_create' => false,
|
||||
'is_initials' => false,
|
||||
'attr_pairs' => array(
|
||||
array('viewBox', '0 0 100 100'),
|
||||
array('fill', 'none'),
|
||||
array('shape-rendering', 'crispEdges'),
|
||||
),
|
||||
'bg_colors' => array(),
|
||||
'bg_type' => array('solid'),
|
||||
'bg_rotation' => array('0', '360'),
|
||||
'body_template' => '@@k:ring@@',
|
||||
'pick_order' => array('ring', 'ringOne', 'ringTwo', 'ringThree', 'ringFour', 'ringFive'),
|
||||
'bool_order' => array(),
|
||||
'color_order' => array(
|
||||
'ring' => 'ringColor',
|
||||
),
|
||||
'pc_has_ints' => true,
|
||||
'pc_meta' => array(
|
||||
array(
|
||||
'group' => 'ring',
|
||||
'values_opt' => 'ring',
|
||||
'width' => 100,
|
||||
'height' => 100,
|
||||
'rotation' => array('opt' => 'ringRotation', 'fallback' => array('0')),
|
||||
'offsetX' => array('opt' => null, 'fallback' => array('0')),
|
||||
'offsetY' => array('opt' => null, 'fallback' => array('0')),
|
||||
),
|
||||
array(
|
||||
'group' => 'ringOne',
|
||||
'values_opt' => 'ringOne',
|
||||
'width' => 100,
|
||||
'height' => 100,
|
||||
'rotation' => array('opt' => 'ringOneRotation', 'fallback' => array('0')),
|
||||
'offsetX' => array('opt' => null, 'fallback' => array('0')),
|
||||
'offsetY' => array('opt' => null, 'fallback' => array('0')),
|
||||
),
|
||||
array(
|
||||
'group' => 'ringTwo',
|
||||
'values_opt' => 'ringTwo',
|
||||
'width' => 100,
|
||||
'height' => 100,
|
||||
'rotation' => array('opt' => 'ringTwoRotation', 'fallback' => array('0')),
|
||||
'offsetX' => array('opt' => null, 'fallback' => array('0')),
|
||||
'offsetY' => array('opt' => null, 'fallback' => array('0')),
|
||||
),
|
||||
array(
|
||||
'group' => 'ringThree',
|
||||
'values_opt' => 'ringThree',
|
||||
'width' => 100,
|
||||
'height' => 100,
|
||||
'rotation' => array('opt' => 'ringThreeRotation', 'fallback' => array('0')),
|
||||
'offsetX' => array('opt' => null, 'fallback' => array('0')),
|
||||
'offsetY' => array('opt' => null, 'fallback' => array('0')),
|
||||
),
|
||||
array(
|
||||
'group' => 'ringFour',
|
||||
'values_opt' => 'ringFour',
|
||||
'width' => 100,
|
||||
'height' => 100,
|
||||
'rotation' => array('opt' => 'ringFourRotation', 'fallback' => array('0')),
|
||||
'offsetX' => array('opt' => null, 'fallback' => array('0')),
|
||||
'offsetY' => array('opt' => null, 'fallback' => array('0')),
|
||||
),
|
||||
array(
|
||||
'group' => 'ringFive',
|
||||
'values_opt' => 'ringFive',
|
||||
'width' => 100,
|
||||
'height' => 100,
|
||||
'rotation' => array('opt' => 'ringFiveRotation', 'fallback' => array('0')),
|
||||
'offsetX' => array('opt' => null, 'fallback' => array('0')),
|
||||
'offsetY' => array('opt' => null, 'fallback' => array('0')),
|
||||
),
|
||||
),
|
||||
'defaults' => array(
|
||||
'ring' => array('container'),
|
||||
'ringColor' => array('ffd54f', '64b5f6', '4dd0e1', 'ff8a65', '9575cd', '81c784', '7986cb', '4fc3f7', 'aed581', 'dce775', 'ffb74d', 'f06292', 'ba68c8', 'e57373', '4db6ac', 'fff176'),
|
||||
'ringFive' => array('full'),
|
||||
'ringFiveRotation' => array('-180', '180'),
|
||||
'ringFour' => array('quarter', 'half'),
|
||||
'ringFourRotation' => array('-180', '180'),
|
||||
'ringOne' => array('half', 'quarter'),
|
||||
'ringOneRotation' => array('-180', '180'),
|
||||
'ringRotation' => array('-180', '180'),
|
||||
'ringThree' => array('quarter', 'half'),
|
||||
'ringThreeRotation' => array('-180', '180'),
|
||||
'ringTwo' => array('quarter', 'half'),
|
||||
'ringTwoRotation' => array('-180', '180'),
|
||||
),
|
||||
'groups' => array(
|
||||
'ring' => array(
|
||||
'container' => '@@k:ringOne@@@@k:ringTwo@@@@k:ringThree@@@@k:ringFour@@@@k:ringFive@@<g transform="matrix(1 0 0 -1 0 100)">@@k:ringOne@@</g><g transform="matrix(1 0 0 -1 0 100)">@@k:ringTwo@@</g><g transform="matrix(1 0 0 -1 0 100)">@@k:ringThree@@</g><g transform="matrix(1 0 0 -1 0 100)">@@k:ringFour@@</g><g transform="matrix(1 0 0 -1 0 100)">@@k:ringFive@@</g><circle cx="50" cy="50" r="45" stroke="#fff" stroke-opacity=".25" stroke-width="10" style="mix-blend-mode:lighten"/><circle cx="50" cy="50" r="25" stroke="#fff" stroke-opacity=".25" stroke-width="10" style="mix-blend-mode:lighten"/><circle cx="50" cy="50" r="10" fill="#fff" fill-opacity=".25" style="mix-blend-mode:lighten"/>',
|
||||
),
|
||||
'ringOne' => array(
|
||||
'half' => '<path d="M90 50h10c0-27.614-22.386-50-50-50S0 22.386 0 50h10c0-22.091 17.909-40 40-40s40 17.909 40 40Z" fill="@@c:ring@@"/>',
|
||||
'quarter' => '<path d="M50 10V0C22.386 0 0 22.386 0 50h10c0-22.091 17.909-40 40-40Z" fill="@@c:ring@@"/>',
|
||||
'eighth' => '<path d="M21.716 21.716C14.477 28.954 10 38.954 10 50H0c0-13.807 5.596-26.307 14.645-35.355l7.07 7.07Z" fill="@@c:ring@@"/>',
|
||||
'full' => '<circle cx="50" cy="50" r="45" stroke="@@c:ring@@" stroke-width="10"/>',
|
||||
),
|
||||
'ringTwo' => array(
|
||||
'eighth' => '<path d="M20 50c0-8.284 3.358-15.784 8.787-21.213l-7.071-7.071C14.477 28.954 10 38.954 10 50h10Z" fill="@@c:ring@@"/>',
|
||||
'quarter' => '<path d="M50 10c-22.091 0-40 17.909-40 40h10c0-16.569 13.431-30 30-30V10Z" fill="@@c:ring@@"/>',
|
||||
'half' => '<path d="M90 50c0-22.091-17.909-40-40-40S10 27.909 10 50h10c0-16.569 13.431-30 30-30 16.569 0 30 13.431 30 30h10Z" fill="@@c:ring@@"/>',
|
||||
'full' => '<circle cx="50" cy="50" r="35" stroke="@@c:ring@@" stroke-width="10"/>',
|
||||
),
|
||||
'ringThree' => array(
|
||||
'eighth' => '<path d="M30 50a19.937 19.937 0 0 1 5.858-14.142l-7.071-7.071C23.357 34.216 20 41.716 20 50h10Z" fill="@@c:ring@@"/>',
|
||||
'quarter' => '<path d="M50 20c-16.569 0-30 13.431-30 30h10c0-11.046 8.954-20 20-20V20Z" fill="@@c:ring@@"/>',
|
||||
'half' => '<path d="M80 50c0-16.569-13.431-30-30-30-16.569 0-30 13.431-30 30h10c0-11.046 8.954-20 20-20s20 8.954 20 20h10Z" fill="@@c:ring@@"/>',
|
||||
'full' => '<circle cx="50" cy="50" r="25" stroke="@@c:ring@@" stroke-width="10"/>',
|
||||
),
|
||||
'ringFour' => array(
|
||||
'eighth' => '<path d="M40 50a9.97 9.97 0 0 1 2.929-7.071l-7.071-7.071A19.937 19.937 0 0 0 30 50h10Z" fill="@@c:ring@@"/>',
|
||||
'quarter' => '<path d="M50 30c-11.046 0-20 8.954-20 20h10c0-5.523 4.477-10 10-10V30Z" fill="@@c:ring@@"/>',
|
||||
'half' => '<path d="M70 50c0-11.046-8.954-20-20-20s-20 8.954-20 20h10c0-5.523 4.477-10 10-10s10 4.477 10 10h10Z" fill="@@c:ring@@"/>',
|
||||
'full' => '<circle cx="50" cy="50" r="15" stroke="@@c:ring@@" stroke-width="10"/>',
|
||||
),
|
||||
'ringFive' => array(
|
||||
'eighth' => '<path d="m42.929 42.929 7.07 7.07H40a9.97 9.97 0 0 1 2.93-7.07Z" fill="@@c:ring@@"/>',
|
||||
'quarter' => '<path d="M50 40v10H40c0-5.523 4.477-10 10-10Z" fill="@@c:ring@@"/>',
|
||||
'half' => '<path d="M60 50c0-5.523-4.477-10-10-10s-10 4.477-10 10h20Z" fill="@@c:ring@@"/>',
|
||||
'full' => '<circle cx="50" cy="50" r="10" fill="@@c:ring@@"/>',
|
||||
),
|
||||
),
|
||||
);
|
||||
Reference in New Issue
Block a user