Version Description
Download this release
Release Info
Developer | shazahm1@hotmail.com |
Plugin | Easy Table of Contents |
Version | 2.0-rc7 |
Comparing to | |
See all releases |
Code changes from version 2.0-rc6 to 2.0-rc7
- README.txt +4 -2
- easy-table-of-contents.php +11 -65
- includes/class.post.php +2 -1
- includes/inc.string-functions.php +296 -0
README.txt
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
Contributors: shazahm1@hotmail.com
|
3 |
Donate link: http://connections-pro.com/
|
4 |
Tags: table of contents, toc
|
5 |
-
Requires at least:
|
6 |
Tested up to: 5.3
|
7 |
Requires PHP: 5.6.20
|
8 |
Stable tag: 1.7
|
@@ -46,7 +46,9 @@ Here are links to documentation pages for several of the premium templates for t
|
|
46 |
|
47 |
= Roadmap =
|
48 |
* Fragment caching for improved performance.
|
49 |
-
*
|
|
|
|
|
50 |
|
51 |
= Credit =
|
52 |
|
2 |
Contributors: shazahm1@hotmail.com
|
3 |
Donate link: http://connections-pro.com/
|
4 |
Tags: table of contents, toc
|
5 |
+
Requires at least: 5.2
|
6 |
Tested up to: 5.3
|
7 |
Requires PHP: 5.6.20
|
8 |
Stable tag: 1.7
|
46 |
|
47 |
= Roadmap =
|
48 |
* Fragment caching for improved performance.
|
49 |
+
* Improve SEO by adding options to add nofollow to TOC link and wrap TOC nav in noindex tag.
|
50 |
+
* Improve accessibility.
|
51 |
+
* Add Bullet and Arrow options for list counter style.
|
52 |
|
53 |
= Credit =
|
54 |
|
easy-table-of-contents.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
* Plugin Name: Easy Table of Contents
|
4 |
* Plugin URI: http://connections-pro.com/
|
5 |
* Description: Adds a user friendly and fully automatic way to create and display a table of contents generated from the page content.
|
6 |
-
* Version: 2.0-
|
7 |
* Author: Steven A. Zahm
|
8 |
* Author URI: http://connections-pro.com/
|
9 |
* Text Domain: easy-table-of-contents
|
@@ -26,9 +26,11 @@
|
|
26 |
* @package Easy Table of Contents
|
27 |
* @category Plugin
|
28 |
* @author Steven A. Zahm
|
29 |
-
* @version 2.0-
|
30 |
*/
|
31 |
|
|
|
|
|
32 |
// Exit if accessed directly
|
33 |
if ( ! defined( 'ABSPATH' ) ) exit;
|
34 |
|
@@ -45,7 +47,7 @@ if ( ! class_exists( 'ezTOC' ) ) {
|
|
45 |
* @since 1.0
|
46 |
* @var string
|
47 |
*/
|
48 |
-
const VERSION = '2.0-
|
49 |
|
50 |
/**
|
51 |
* Stores the instance of this class.
|
@@ -130,6 +132,7 @@ if ( ! class_exists( 'ezTOC' ) ) {
|
|
130 |
require_once( EZ_TOC_PATH . 'includes/class.post.php' );
|
131 |
require_once( EZ_TOC_PATH . 'includes/class.widget-toc.php' );
|
132 |
require_once( EZ_TOC_PATH . 'includes/inc.functions.php' );
|
|
|
133 |
|
134 |
require_once( EZ_TOC_PATH . 'includes/inc.plugin-compatibility.php' );
|
135 |
}
|
@@ -323,63 +326,6 @@ if ( ! class_exists( 'ezTOC' ) ) {
|
|
323 |
}
|
324 |
}
|
325 |
|
326 |
-
/**
|
327 |
-
* Returns a string with all items from the $find array replaced with their matching
|
328 |
-
* items in the $replace array. This does a one to one replacement (rather than globally).
|
329 |
-
*
|
330 |
-
* This function is multibyte safe.
|
331 |
-
*
|
332 |
-
* $find and $replace are arrays, $string is the haystack. All variables are passed by reference.
|
333 |
-
*
|
334 |
-
* @access private
|
335 |
-
* @since 1.0
|
336 |
-
* @static
|
337 |
-
*
|
338 |
-
* @param bool $find
|
339 |
-
* @param bool $replace
|
340 |
-
* @param string $string
|
341 |
-
*
|
342 |
-
* @return mixed|string
|
343 |
-
*/
|
344 |
-
private static function mb_find_replace( &$find = false, &$replace = false, &$string = '' ) {
|
345 |
-
|
346 |
-
if ( is_array( $find ) && is_array( $replace ) && $string ) {
|
347 |
-
|
348 |
-
// check if multibyte strings are supported
|
349 |
-
if ( function_exists( 'mb_strpos' ) ) {
|
350 |
-
|
351 |
-
for ( $i = 0; $i < count( $find ); $i ++ ) {
|
352 |
-
|
353 |
-
$string = mb_substr(
|
354 |
-
$string,
|
355 |
-
0,
|
356 |
-
mb_strpos( $string, $find[ $i ] )
|
357 |
-
) . // everything before $find
|
358 |
-
$replace[ $i ] . // its replacement
|
359 |
-
mb_substr(
|
360 |
-
$string,
|
361 |
-
mb_strpos( $string, $find[ $i ] ) + mb_strlen( $find[ $i ] )
|
362 |
-
) // everything after $find
|
363 |
-
;
|
364 |
-
}
|
365 |
-
|
366 |
-
} else {
|
367 |
-
|
368 |
-
for ( $i = 0; $i < count( $find ); $i ++ ) {
|
369 |
-
|
370 |
-
$string = substr_replace(
|
371 |
-
$string,
|
372 |
-
$replace[ $i ],
|
373 |
-
strpos( $string, $find[ $i ] ),
|
374 |
-
strlen( $find[ $i ] )
|
375 |
-
);
|
376 |
-
}
|
377 |
-
}
|
378 |
-
}
|
379 |
-
|
380 |
-
return $string;
|
381 |
-
}
|
382 |
-
|
383 |
/**
|
384 |
* Array search deep.
|
385 |
*
|
@@ -633,29 +579,29 @@ if ( ! class_exists( 'ezTOC' ) ) {
|
|
633 |
// if shortcode used or post not eligible, return content with anchored headings
|
634 |
if ( strpos( $content, 'ez-toc-container' ) || ! $is_eligible ) {
|
635 |
|
636 |
-
return
|
637 |
}
|
638 |
|
639 |
// else also add toc to content
|
640 |
switch ( ezTOC_Option::get( 'position' ) ) {
|
641 |
|
642 |
case 'top':
|
643 |
-
$content = $html .
|
644 |
break;
|
645 |
|
646 |
case 'bottom':
|
647 |
-
$content =
|
648 |
break;
|
649 |
|
650 |
case 'after':
|
651 |
$replace[0] = $replace[0] . $html;
|
652 |
-
$content =
|
653 |
break;
|
654 |
|
655 |
case 'before':
|
656 |
default:
|
657 |
$replace[0] = $html . $replace[0];
|
658 |
-
$content =
|
659 |
}
|
660 |
|
661 |
return $content;
|
3 |
* Plugin Name: Easy Table of Contents
|
4 |
* Plugin URI: http://connections-pro.com/
|
5 |
* Description: Adds a user friendly and fully automatic way to create and display a table of contents generated from the page content.
|
6 |
+
* Version: 2.0-rc7
|
7 |
* Author: Steven A. Zahm
|
8 |
* Author URI: http://connections-pro.com/
|
9 |
* Text Domain: easy-table-of-contents
|
26 |
* @package Easy Table of Contents
|
27 |
* @category Plugin
|
28 |
* @author Steven A. Zahm
|
29 |
+
* @version 2.0-rc7
|
30 |
*/
|
31 |
|
32 |
+
use function Easy_Plugins\Table_Of_Contents\String\mb_find_replace;
|
33 |
+
|
34 |
// Exit if accessed directly
|
35 |
if ( ! defined( 'ABSPATH' ) ) exit;
|
36 |
|
47 |
* @since 1.0
|
48 |
* @var string
|
49 |
*/
|
50 |
+
const VERSION = '2.0-rc7';
|
51 |
|
52 |
/**
|
53 |
* Stores the instance of this class.
|
132 |
require_once( EZ_TOC_PATH . 'includes/class.post.php' );
|
133 |
require_once( EZ_TOC_PATH . 'includes/class.widget-toc.php' );
|
134 |
require_once( EZ_TOC_PATH . 'includes/inc.functions.php' );
|
135 |
+
require_once( EZ_TOC_PATH . 'includes/inc.string-functions.php' );
|
136 |
|
137 |
require_once( EZ_TOC_PATH . 'includes/inc.plugin-compatibility.php' );
|
138 |
}
|
326 |
}
|
327 |
}
|
328 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
329 |
/**
|
330 |
* Array search deep.
|
331 |
*
|
579 |
// if shortcode used or post not eligible, return content with anchored headings
|
580 |
if ( strpos( $content, 'ez-toc-container' ) || ! $is_eligible ) {
|
581 |
|
582 |
+
return mb_find_replace( $find, $replace, $content );
|
583 |
}
|
584 |
|
585 |
// else also add toc to content
|
586 |
switch ( ezTOC_Option::get( 'position' ) ) {
|
587 |
|
588 |
case 'top':
|
589 |
+
$content = $html . mb_find_replace( $find, $replace, $content );
|
590 |
break;
|
591 |
|
592 |
case 'bottom':
|
593 |
+
$content = mb_find_replace( $find, $replace, $content ) . $html;
|
594 |
break;
|
595 |
|
596 |
case 'after':
|
597 |
$replace[0] = $replace[0] . $html;
|
598 |
+
$content = mb_find_replace( $find, $replace, $content );
|
599 |
break;
|
600 |
|
601 |
case 'before':
|
602 |
default:
|
603 |
$replace[0] = $html . $replace[0];
|
604 |
+
$content = mb_find_replace( $find, $replace, $content );
|
605 |
}
|
606 |
|
607 |
return $content;
|
includes/class.post.php
CHANGED
@@ -292,7 +292,8 @@ class ezTOC_Post {
|
|
292 |
*
|
293 |
* @see wpautop()
|
294 |
*/
|
295 |
-
|
|
|
296 |
|
297 |
$pages[ $page ] = array(
|
298 |
'headings' => $this->extractHeadings( $eligibleContent ),
|
292 |
*
|
293 |
* @see wpautop()
|
294 |
*/
|
295 |
+
//$eligibleContent = str_replace( array( '<br>', '<br/>' ), array( '<br />' ), $eligibleContent );
|
296 |
+
$eligibleContent = \Easy_Plugins\Table_Of_Contents\String\force_balance_tags( $eligibleContent );
|
297 |
|
298 |
$pages[ $page ] = array(
|
299 |
'headings' => $this->extractHeadings( $eligibleContent ),
|
includes/inc.string-functions.php
ADDED
@@ -0,0 +1,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Easy_Plugins\Table_Of_Contents\String;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Pulled from WordPress formatting functions.
|
7 |
+
*
|
8 |
+
* Edited to add space before self closing tags.
|
9 |
+
*
|
10 |
+
* @since 2.0
|
11 |
+
*
|
12 |
+
* @param string $text
|
13 |
+
*
|
14 |
+
* @return string|string[]
|
15 |
+
*/
|
16 |
+
function force_balance_tags( $text ) {
|
17 |
+
$tagstack = array();
|
18 |
+
$stacksize = 0;
|
19 |
+
$tagqueue = '';
|
20 |
+
$newtext = '';
|
21 |
+
// Known single-entity/self-closing tags
|
22 |
+
$single_tags = array( 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param', 'source' );
|
23 |
+
// Tags that can be immediately nested within themselves
|
24 |
+
$nestable_tags = array( 'blockquote', 'div', 'object', 'q', 'span' );
|
25 |
+
|
26 |
+
// WP bug fix for comments - in case you REALLY meant to type '< !--'
|
27 |
+
$text = str_replace( '< !--', '< !--', $text );
|
28 |
+
// WP bug fix for LOVE <3 (and other situations with '<' before a number)
|
29 |
+
$text = preg_replace( '#<([0-9]{1})#', '<$1', $text );
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Matches supported tags.
|
33 |
+
*
|
34 |
+
* To get the pattern as a string without the comments paste into a PHP
|
35 |
+
* REPL like `php -a`.
|
36 |
+
*
|
37 |
+
* @see https://html.spec.whatwg.org/#elements-2
|
38 |
+
* @see https://w3c.github.io/webcomponents/spec/custom/#valid-custom-element-name
|
39 |
+
*
|
40 |
+
* @example
|
41 |
+
* ~# php -a
|
42 |
+
* php > $s = [paste copied contents of expression below including parentheses];
|
43 |
+
* php > echo $s;
|
44 |
+
*/
|
45 |
+
$tag_pattern = (
|
46 |
+
'#<' . // Start with an opening bracket.
|
47 |
+
'(/?)' . // Group 1 - If it's a closing tag it'll have a leading slash.
|
48 |
+
'(' . // Group 2 - Tag name.
|
49 |
+
// Custom element tags have more lenient rules than HTML tag names.
|
50 |
+
'(?:[a-z](?:[a-z0-9._]*)-(?:[a-z0-9._-]+)+)' .
|
51 |
+
'|' .
|
52 |
+
// Traditional tag rules approximate HTML tag names.
|
53 |
+
'(?:[\w:]+)' .
|
54 |
+
')' .
|
55 |
+
'(?:' .
|
56 |
+
// We either immediately close the tag with its '>' and have nothing here.
|
57 |
+
'\s*' .
|
58 |
+
'(/?)' . // Group 3 - "attributes" for empty tag.
|
59 |
+
'|' .
|
60 |
+
// Or we must start with space characters to separate the tag name from the attributes (or whitespace).
|
61 |
+
'(\s+)' . // Group 4 - Pre-attribute whitespace.
|
62 |
+
'([^>]*)' . // Group 5 - Attributes.
|
63 |
+
')' .
|
64 |
+
'>#' // End with a closing bracket.
|
65 |
+
);
|
66 |
+
|
67 |
+
while ( preg_match( $tag_pattern, $text, $regex ) ) {
|
68 |
+
$full_match = $regex[0];
|
69 |
+
$has_leading_slash = ! empty( $regex[1] );
|
70 |
+
$tag_name = $regex[2];
|
71 |
+
$tag = strtolower( $tag_name );
|
72 |
+
$is_single_tag = in_array( $tag, $single_tags, true );
|
73 |
+
$pre_attribute_ws = isset( $regex[4] ) ? $regex[4] : '';
|
74 |
+
$attributes = trim( isset( $regex[5] ) ? $regex[5] : $regex[3] );
|
75 |
+
$has_self_closer = '/' === substr( $attributes, -1 );
|
76 |
+
|
77 |
+
$newtext .= $tagqueue;
|
78 |
+
|
79 |
+
$i = strpos( $text, $full_match );
|
80 |
+
$l = strlen( $full_match );
|
81 |
+
|
82 |
+
// Clear the shifter.
|
83 |
+
$tagqueue = '';
|
84 |
+
if ( $has_leading_slash ) { // End Tag.
|
85 |
+
// If too many closing tags.
|
86 |
+
if ( $stacksize <= 0 ) {
|
87 |
+
$tag = '';
|
88 |
+
// Or close to be safe $tag = '/' . $tag.
|
89 |
+
|
90 |
+
// If stacktop value = tag close value, then pop.
|
91 |
+
} elseif ( $tagstack[ $stacksize - 1 ] === $tag ) { // Found closing tag.
|
92 |
+
$tag = '</' . $tag . '>'; // Close Tag.
|
93 |
+
array_pop( $tagstack );
|
94 |
+
$stacksize--;
|
95 |
+
} else { // Closing tag not at top, search for it.
|
96 |
+
for ( $j = $stacksize - 1; $j >= 0; $j-- ) {
|
97 |
+
if ( $tagstack[ $j ] === $tag ) {
|
98 |
+
// Add tag to tagqueue.
|
99 |
+
for ( $k = $stacksize - 1; $k >= $j; $k-- ) {
|
100 |
+
$tagqueue .= '</' . array_pop( $tagstack ) . '>';
|
101 |
+
$stacksize--;
|
102 |
+
}
|
103 |
+
break;
|
104 |
+
}
|
105 |
+
}
|
106 |
+
$tag = '';
|
107 |
+
}
|
108 |
+
} else { // Begin Tag.
|
109 |
+
if ( $has_self_closer ) { // If it presents itself as a self-closing tag...
|
110 |
+
// ...but it isn't a known single-entity self-closing tag, then don't let it be treated as such and
|
111 |
+
// immediately close it with a closing tag (the tag will encapsulate no text as a result)
|
112 |
+
if ( ! $is_single_tag ) {
|
113 |
+
$attributes = trim( substr( $attributes, 0, -1 ) ) . "></$tag";
|
114 |
+
}
|
115 |
+
} elseif ( $is_single_tag ) { // ElseIf it's a known single-entity tag but it doesn't close itself, do so
|
116 |
+
$pre_attribute_ws = ' ';
|
117 |
+
$attributes .= 0 < strlen( $attributes ) ? ' /' : '/'; // EDIT: If there are attributes, add space before closing tag to match how WP insert br, hr and img tags.
|
118 |
+
} else { // It's not a single-entity tag.
|
119 |
+
// If the top of the stack is the same as the tag we want to push, close previous tag.
|
120 |
+
if ( $stacksize > 0 && ! in_array( $tag, $nestable_tags, true ) && $tagstack[ $stacksize - 1 ] === $tag ) {
|
121 |
+
$tagqueue = '</' . array_pop( $tagstack ) . '>';
|
122 |
+
$stacksize--;
|
123 |
+
}
|
124 |
+
$stacksize = array_push( $tagstack, $tag );
|
125 |
+
}
|
126 |
+
|
127 |
+
// Attributes.
|
128 |
+
if ( $has_self_closer && $is_single_tag ) {
|
129 |
+
// We need some space - avoid <br/> and prefer <br />.
|
130 |
+
$pre_attribute_ws = ' ';
|
131 |
+
}
|
132 |
+
|
133 |
+
$tag = '<' . $tag . $pre_attribute_ws . $attributes . '>';
|
134 |
+
// If already queuing a close tag, then put this tag on too.
|
135 |
+
if ( ! empty( $tagqueue ) ) {
|
136 |
+
$tagqueue .= $tag;
|
137 |
+
$tag = '';
|
138 |
+
}
|
139 |
+
}
|
140 |
+
$newtext .= substr( $text, 0, $i ) . $tag;
|
141 |
+
$text = substr( $text, $i + $l );
|
142 |
+
}
|
143 |
+
|
144 |
+
// Clear Tag Queue.
|
145 |
+
$newtext .= $tagqueue;
|
146 |
+
|
147 |
+
// Add remaining text.
|
148 |
+
$newtext .= $text;
|
149 |
+
|
150 |
+
while ( $x = array_pop( $tagstack ) ) {
|
151 |
+
$newtext .= '</' . $x . '>'; // Add remaining tags to close.
|
152 |
+
}
|
153 |
+
|
154 |
+
// WP fix for the bug with HTML comments.
|
155 |
+
$newtext = str_replace( '< !--', '<!--', $newtext );
|
156 |
+
$newtext = str_replace( '< !--', '< !--', $newtext );
|
157 |
+
|
158 |
+
return $newtext;
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Multibyte substr_replace(). The mbstring library does not come with a multibyte equivalent of substr_replace().
|
163 |
+
* This function behaves exactly like substr_replace() even when the arguments are arrays.
|
164 |
+
*
|
165 |
+
* @link https://gist.github.com/stemar/8287074
|
166 |
+
*
|
167 |
+
* @since 2.0
|
168 |
+
*
|
169 |
+
* @param $string
|
170 |
+
* @param $replacement
|
171 |
+
* @param $start
|
172 |
+
* @param null $length
|
173 |
+
*
|
174 |
+
* @return array|string
|
175 |
+
*/
|
176 |
+
function mb_substr_replace( $string, $replacement, $start, $length = null ) {
|
177 |
+
|
178 |
+
if ( is_array( $string ) ) {
|
179 |
+
|
180 |
+
$num = count( $string );
|
181 |
+
|
182 |
+
// $replacement
|
183 |
+
$replacement = is_array( $replacement ) ? array_slice( $replacement, 0, $num ) : array_pad( array( $replacement ), $num, $replacement );
|
184 |
+
|
185 |
+
// $start
|
186 |
+
if ( is_array( $start ) ) {
|
187 |
+
$start = array_slice( $start, 0, $num );
|
188 |
+
foreach ( $start as $key => $value ) {
|
189 |
+
$start[ $key ] = is_int( $value ) ? $value : 0;
|
190 |
+
}
|
191 |
+
} else {
|
192 |
+
$start = array_pad( array( $start ), $num, $start );
|
193 |
+
}
|
194 |
+
|
195 |
+
// $length
|
196 |
+
if ( ! isset( $length ) ) {
|
197 |
+
$length = array_fill( 0, $num, 0 );
|
198 |
+
} elseif ( is_array( $length ) ) {
|
199 |
+
$length = array_slice( $length, 0, $num );
|
200 |
+
foreach ( $length as $key => $value ) {
|
201 |
+
$length[ $key ] = isset( $value ) ? ( is_int( $value ) ? $value : $num ) : 0;
|
202 |
+
}
|
203 |
+
} else {
|
204 |
+
$length = array_pad( array( $length ), $num, $length );
|
205 |
+
}
|
206 |
+
|
207 |
+
// Recursive call
|
208 |
+
return array_map( __FUNCTION__, $string, $replacement, $start, $length );
|
209 |
+
}
|
210 |
+
|
211 |
+
preg_match_all( '/./us', (string) $string, $smatches );
|
212 |
+
preg_match_all( '/./us', (string) $replacement, $rmatches );
|
213 |
+
|
214 |
+
if ( $length === null ) {
|
215 |
+
|
216 |
+
$length = mb_strlen( $string );
|
217 |
+
}
|
218 |
+
|
219 |
+
array_splice( $smatches[0], $start, $length, $rmatches[0] );
|
220 |
+
|
221 |
+
return join( $smatches[0] );
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Returns a string with all items from the $find array replaced with their matching
|
226 |
+
* items in the $replace array. This does a one to one replacement (rather than globally).
|
227 |
+
*
|
228 |
+
* This function is multibyte safe.
|
229 |
+
*
|
230 |
+
* $find and $replace are arrays, $string is the haystack. All variables are passed by reference.
|
231 |
+
*
|
232 |
+
* @since 1.0
|
233 |
+
*
|
234 |
+
* @param bool $find
|
235 |
+
* @param bool $replace
|
236 |
+
* @param string $string
|
237 |
+
*
|
238 |
+
* @return mixed|string
|
239 |
+
*/
|
240 |
+
function mb_find_replace( &$find = false, &$replace = false, &$string = '' ) {
|
241 |
+
|
242 |
+
if ( is_array( $find ) && is_array( $replace ) && $string ) {
|
243 |
+
|
244 |
+
// check if multibyte strings are supported
|
245 |
+
if ( function_exists( 'mb_strpos' ) ) {
|
246 |
+
|
247 |
+
//for ( $i = 0; $i < count( $find ); $i ++ ) {
|
248 |
+
//
|
249 |
+
// $string = mb_substr(
|
250 |
+
// $string,
|
251 |
+
// 0,
|
252 |
+
// mb_strpos( $string, $find[ $i ] )
|
253 |
+
// ) . // everything before $find
|
254 |
+
// $replace[ $i ] . // its replacement
|
255 |
+
// mb_substr(
|
256 |
+
// $string,
|
257 |
+
// mb_strpos( $string, $find[ $i ] ) + mb_strlen( $find[ $i ] )
|
258 |
+
// ) // everything after $find
|
259 |
+
// ;
|
260 |
+
//}
|
261 |
+
|
262 |
+
for ( $i = 0; $i < count( $find ); $i ++ ) {
|
263 |
+
|
264 |
+
$start = mb_strpos( $string, $find[ $i ] );
|
265 |
+
$length = mb_strlen( $find[ $i ] );
|
266 |
+
|
267 |
+
/*
|
268 |
+
* `mb_strpos()` can return `false`. Only process `mb_substr_replace()` if position in string is found.
|
269 |
+
*/
|
270 |
+
if ( is_int( $start ) ) {
|
271 |
+
|
272 |
+
$string = mb_substr_replace( $string, $replace[ $i ], $start, $length );
|
273 |
+
}
|
274 |
+
|
275 |
+
}
|
276 |
+
|
277 |
+
} else {
|
278 |
+
|
279 |
+
for ( $i = 0; $i < count( $find ); $i ++ ) {
|
280 |
+
|
281 |
+
$start = strpos( $string, $find[ $i ] );
|
282 |
+
$length = strlen( $find[ $i ] );
|
283 |
+
|
284 |
+
/*
|
285 |
+
* `strpos()` can return `false`. Only process `substr_replace()` if position in string is found.
|
286 |
+
*/
|
287 |
+
if ( is_int( $start ) ) {
|
288 |
+
|
289 |
+
$string = substr_replace( $string, $replace[ $i ], $start, $length );
|
290 |
+
}
|
291 |
+
}
|
292 |
+
}
|
293 |
+
}
|
294 |
+
|
295 |
+
return $string;
|
296 |
+
}
|