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。到这里,我们可以关灯睡觉了。