1 <?php
2 namespace apemsel\AttributedString;
3
4 5 6 7 8 9 10
11 class MutableAttributedString extends AttributedString
12 {
13 14 15 16 17 18
19 public function insert($pos, $string) {
20 $length = mb_strlen($string, "utf-8");
21
22 if ($pos == $this->length) {
23 $this->string .= $string;
24 } else {
25 $this->string = self::mb_substr_replace($this->string, $string, $pos, 0);
26 }
27
28 $this->length += $length;
29 $this->byte2Char = [];
30
31 foreach ($this->attributes as $attribute => &$map) {
32
33 $state = false;
34 $maxPos = count($map) - 1;
35 $leftState = $map[min($maxPos, $pos)];
36 $rightState = $map[min($maxPos, $pos + 1)];
37
38 if ($leftState == $rightState) {
39 $state = $leftState;
40 }
41
42 array_splice($map, $pos, 0, array_fill(0, $length, $state));
43 }
44 }
45
46 47 48 49 50 51
52 public function delete($pos, $length) {
53 $leftPart = "";
54 if ($pos >= 0) {
55 $leftPart = mb_substr($this->string, 0, $pos, "utf-8");
56 }
57
58 $rightPart = "";
59 if ($pos + $length < $this->length) {
60 $rightPart = mb_substr($this->string, $pos + $length, NULL, "utf-8");
61 }
62
63 $this->string = $leftPart.$rightPart;
64 $this->length -= $length;
65
66 foreach ($this->attributes as $attribute => &$map) {
67 array_splice($map, $pos, $length);
68 }
69 }
70
71 72 73 74 75 76 77 78 79 80
81 protected static function mb_substr_replace($string, $replacement, $start, $length = NULL) {
82
83 if (is_array($string)) {
84 $num = count($string);
85
86 $replacement = is_array($replacement) ? array_slice($replacement, 0, $num) : array_pad(array($replacement), $num, $replacement);
87
88 if (is_array($start)) {
89 $start = array_slice($start, 0, $num);
90 foreach ($start as $key => $value) {
91 $start[$key] = is_int($value) ? $value : 0;
92 }
93 } else {
94 $start = array_pad(array($start), $num, $start);
95 }
96
97 if (!isset($length)) {
98 $length = array_fill(0, $num, 0);
99 } elseif (is_array($length)) {
100 $length = array_slice($length, 0, $num);
101 foreach ($length as $key => $value)
102 $length[$key] = isset($value) ? (is_int($value) ? $value : $num) : 0;
103 } else {
104 $length = array_pad(array($length), $num, $length);
105 }
106
107 return array_map(__FUNCTION__, $string, $replacement, $start, $length);
108 }
109 preg_match_all('/./us', (string)$string, $smatches);
110 preg_match_all('/./us', (string)$replacement, $rmatches);
111 if ($length === NULL) $length = mb_strlen($string, "utf-8");
112 array_splice($smatches[0], $start, $length, $rmatches[0]);
113
114 return join($smatches[0]);
115 }
116
117
118
119 120 121 122 123
124 public function offsetSet($offset, $value) {
125 $this->string = self::mb_substr_replace($this->string, $value, $offset, mb_strlen($value, "utf-8"));
126 }
127
128 129 130 131 132
133 public function offsetUnset($offset) {
134 $this->delete($offset, 1);
135 }
136 }
137