乌徒帮技术范WordPressWordPress开发 › 修改WordPress文章别名post_name的最大长度

修改WordPress文章别名post_name的最大长度

分类:WordPress开发

首先,让我们先看下WordPress中文论坛的一个帖子,这个帖子某“妹子”提问如何使得默认的文章别名的长度增加,以免被拦腰截断。为什么会有这个需求呢?

为什么要增加文章别名的长度?

如今的wordpress不一样了,中文用户使用wordpress的情况越来越多,然而实际上wordpress内核中没有针对中文用户的优化,(虽然有中文团队,但工作仅限于翻译、适应中文网站的抓取等等,但从内核上提高对中文用户的友好度几乎没有,)面对这种情况,当中文用户由于中文网站的某些与英文网站不同的地方就产生了矛盾。

其中最突出的矛盾在于中文的词语和词语之间基本不需要分隔符,而英文词语之间需要用空格隔开,中文标点和英文标点所占的字节位也不同,中文字符所占的长度更差等等,总之中文用户在使用wordpress时,其实只能靠开发者自己针对当前的中文需求继续开发。目前尚无统一的wordpress中文标准,这就使得很多情况下为了适应中文的时候,中文开发者没有足够的资源去修改或开发,只能靠自己摸索,很多情况下找不到答案。

呼吁建立中国wordpress开发者标准!

那么为什么要增加文章别名的长度呢?

我建立了自己的SEO博客,随着对搜索引擎的观察,我希望在自己的URL中放入中文字,但是情况并不很好,当我这样去做之后,发现很多理想的情况是不能实现的,例如本文重点要解决的文章别名的长度。当我们使用%postname%作为文章的URL时,有时我们还使用中文,但如果这个文章的标题特别长,那么在不经意的情况下,你的别名就被拦腰截断,成了四不像。因此,我们其实希望中文的别名长度能够增加,要知道中文字符和英文有着本质的区别,英文一大堆单词,中文可能只需要几个字就表达清楚了,但中文一个字却要占英文多个单词的位置,这对中文是不公平的。

幸好,我找到了解决的办法。

如何增加文章别名的长度?

wordpress中对别名长度的限制通过多个方面来进行限制,首先是数据库,在wordpress数据库表$wpdb->posts中,post_name字段的类型是varchar(200),也就是说文章别名只能在数据库中容纳200个字符,但我们知道,中文别名在数据库中使用了urlencode加密,一个中文标题经过加密后就会变的很长很长,据我观察,这200个字符位置最多容纳下22个左右的中文字,而如果我们的文章需要更长的位置怎么办呢?

修改$wpdb->posts字段的长度限制,使用phpmyadmin进入post_name字段的编辑界面,修改200为500,甚至更长。这样,在数据库中你就可以让别名容纳更多的字数了。

然而,这只是第一步,你要知道,虽然数据库中你已经修改了,但wordpress系统本身还不让你这么做,在提交文章的时候,它还会先检查一下,如果你的标题字符超过了200,它还得把你的标题给截断,非常残忍。我们通过一个HOOK来调整wordpress的这个行为。一切得从/wp-includes/formatting.php开始。

/wp-includes/formatting.php是wordpress系统的字符格式化函数库,里面可以对wordpress系统中的字符进行检查、转换、处理等。我们找到sanitize_title函数,它将完成很多和标题相关的字符串处理动作。可是跟别名相关的还跟它没什么关系,我们找到sanitize_title_with_dashes函数,它才是别名处理的罪魁祸首,在执行sanitize_title函数时sanitize_title_with_dashes将发生动作,我们来看下它的源码:

function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'display' ) {
?? ?$title = strip_tags($title);
?? ?// Preserve escaped octets.
?? ?$title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title);
?? ?// Remove percent signs that are not part of an octet.
?? ?$title = str_replace('%', '', $title);
?? ?// Restore octets.
?? ?$title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title);

?? ?if (seems_utf8($title)) {
?? ??? ?if (function_exists('mb_strtolower')) {
?? ??? ??? ?$title = mb_strtolower($title, 'UTF-8');
?? ??? ?}
?? ??? ?$title = utf8_uri_encode($title, 200);
?? ?}

?? ?$title = strtolower($title);
?? ?$title = preg_replace('/&.+?;/', '', $title); // kill entities
?? ?$title = str_replace('.', '-', $title);

?? ?if ( 'save' == $context ) {
?? ??? ?// Convert nbsp, ndash and mdash to hyphens
?? ??? ?$title = str_replace( array( '%c2%a0', '%e2%80%93', '%e2%80%94' ), '-', $title );

?? ??? ?// Strip these characters entirely
?? ??? ?$title = str_replace( array(
?? ??? ??? ?// iexcl and iquest
?? ??? ??? ?'%c2%a1', '%c2%bf',
?? ??? ??? ?// angle quotes
?? ??? ??? ?'%c2%ab', '%c2%bb', '%e2%80%b9', '%e2%80%ba',
?? ??? ??? ?// curly quotes
?? ??? ??? ?'%e2%80%98', '%e2%80%99', '%e2%80%9c', '%e2%80%9d',
?? ??? ??? ?'%e2%80%9a', '%e2%80%9b', '%e2%80%9e', '%e2%80%9f',
?? ??? ??? ?// copy, reg, deg, hellip and trade
?? ??? ??? ?'%c2%a9', '%c2%ae', '%c2%b0', '%e2%80%a6', '%e2%84%a2',
?? ??? ??? ?// acute accents
?? ??? ??? ?'%c2%b4', '%cb%8a', '%cc%81', '%cd%81',
?? ??? ??? ?// grave accent, macron, caron
?? ??? ??? ?'%cc%80', '%cc%84', '%cc%8c',
?? ??? ?), '', $title );

?? ??? ?// Convert times to x
?? ??? ?$title = str_replace( '%c3%97', 'x', $title );
?? ?}

?? ?$title = preg_replace('/[^%a-z0-9 _-]/', '', $title);
?? ?$title = preg_replace('/\s+/', '-', $title);
?? ?$title = preg_replace('|-+|', '-', $title);
?? ?$title = trim($title, '-');

?? ?return $title;
}

你就会发现,最后得以保存在数据库中的post_name是靠它来格式化完成的,我们必须修改它才能实现我们的目的。那怎么办?你看到我上面用红色标记的地方了吧,把200改为500,覆盖掉原来的formatting.php文件,好了,到后台发布一篇文章(中文标题,很长很长)试试。

这个原理也很简单,原本处理时会用到utf8_uri_encode函数,它对字符串进行识别,判断其所花费的字节位,并做参数中给定数值的截断,我们把它改大不就增加了文章别名的长度了吗(中文的情况下,英文没有测试)?

当然,你会说使用修改wordpress系统文件的方法不可靠,一旦升级系统就会失效。我们也提供一个HOOK的方法,原理很简单,把sanitize_title_with_dashes从事件流中去除,使用新的同样的经过修改的函数(把下面的代码放到functions.php中):

remove_filter( 'sanitize_title', 'sanitize_title_with_dashes' );
add_filter( 'sanitize_title', 'sanitize_title_with_dashes_longer_post_name' );
function sanitize_title_with_dashes_longer_post_name(?$title, $raw_title = '', $context = 'display' ) {
?? ?$title = strip_tags($title);
?? ?// Preserve escaped octets.
?? ?$title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title);
?? ?// Remove percent signs that are not part of an octet.
?? ?$title = str_replace('%', '', $title);
?? ?// Restore octets.
?? ?$title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title);

?? ?if (seems_utf8($title)) {
?? ??? ?if (function_exists('mb_strtolower')) {
?? ??? ??? ?$title = mb_strtolower($title, 'UTF-8');
?? ??? ?}
?? ??? ?$title = utf8_uri_encode($title, 500);
?? ?}

?? ?$title = strtolower($title);
?? ?$title = preg_replace('/&.+?;/', '', $title); // kill entities
?? ?$title = str_replace('.', '-', $title);

?? ?if ( 'save' == $context ) {
?? ??? ?// Convert nbsp, ndash and mdash to hyphens
?? ??? ?$title = str_replace( array( '%c2%a0', '%e2%80%93', '%e2%80%94' ), '-', $title );

?? ??? ?// Strip these characters entirely
?? ??? ?$title = str_replace( array(
?? ??? ??? ?// iexcl and iquest
?? ??? ??? ?'%c2%a1', '%c2%bf',
?? ??? ??? ?// angle quotes
?? ??? ??? ?'%c2%ab', '%c2%bb', '%e2%80%b9', '%e2%80%ba',
?? ??? ??? ?// curly quotes
?? ??? ??? ?'%e2%80%98', '%e2%80%99', '%e2%80%9c', '%e2%80%9d',
?? ??? ??? ?'%e2%80%9a', '%e2%80%9b', '%e2%80%9e', '%e2%80%9f',
?? ??? ??? ?// copy, reg, deg, hellip and trade
?? ??? ??? ?'%c2%a9', '%c2%ae', '%c2%b0', '%e2%80%a6', '%e2%84%a2',
?? ??? ??? ?// acute accents
?? ??? ??? ?'%c2%b4', '%cb%8a', '%cc%81', '%cd%81',
?? ??? ??? ?// grave accent, macron, caron
?? ??? ??? ?'%cc%80', '%cc%84', '%cc%8c',
?? ??? ?), '', $title );

?? ??? ?// Convert times to x
?? ??? ?$title = str_replace( '%c3%97', 'x', $title );
?? ?}

?? ?$title = preg_replace('/[^%a-z0-9 _-]/', '', $title);
?? ?$title = preg_replace('/\s+/', '-', $title);
?? ?$title = preg_replace('|-+|', '-', $title);
?? ?$title = trim($title, '-');

?? ?return $title;
}

其实对于整个标题处理过程而言,就换了一个数字,却需要写这么多代码。记住:要让它生效,你的整个wordpress中,包括你的主题插件中,没有再对此做规定,否则一旦某一处小于500,那么会以小的一处作为标准。

看到这里你或许以为已经完事OK,可以洗洗睡了。然而,你不得不听我把故事讲完,在/wp-includes/post.php中有这么一段代码:

function _truncate_post_slug( $slug, $length = 200 ) {
?? ?if ( strlen( $slug ) > $length ) {
?? ??? ?$decoded_slug = urldecode( $slug );
?? ??? ?if ( $decoded_slug === $slug )
?? ??? ??? ?$slug = substr( $slug, 0, $length );
?? ??? ?else
?? ??? ??? ?$slug = utf8_uri_encode( $decoded_slug, $length );
?? ?}

?? ?return rtrim( $slug, '-' );
}

它好像也要做点什么事,我也没有必要再深究了,把200果断改为500。到这里,我们可以关灯睡觉了。

填写个人信息,赶快回复吧!