Skip to content

Commit d95482c

Browse files
committed
introduce AutoString factory
and Win1252String as a separate class
1 parent 8c4be0a commit d95482c

3 files changed

Lines changed: 96 additions & 0 deletions

File tree

‎src/AutoString.php‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace StringObject;
4+
5+
use StringObject\Utf8\Parser;
6+
7+
class AutoString
8+
{
9+
public static function from(string $input): AsciiString|Win1252String|Utf8String
10+
{
11+
if (\preg_match('/^[\x00-\x7F]*$/', $input)) {
12+
return new AsciiString($input);
13+
}
14+
15+
$mbstring = \extension_loaded('mbstring');
16+
if (
17+
($mbstring && \mb_check_encoding($input, 'UTF-8'))
18+
|| (!$mbstring && \preg_match('//u', $input))
19+
) {
20+
return new Utf8String($input);
21+
}
22+
23+
if (self::isLikelyWin1252($input)) {
24+
return new Win1252String($input);
25+
}
26+
27+
throw new \InvalidArgumentException('Input string is not valid ASCII, Win1252, or UTF-8');
28+
}
29+
30+
private static function isLikelyWin1252(string $input): bool
31+
{
32+
// Fail early if any explicitly invalid C1 positions are present
33+
if (\preg_match('/[\x81\x8D\x8F\x90\x9D]/', $input)) {
34+
return false;
35+
}
36+
37+
// If it contains any other C1 bytes (0x80-0x9F), assume 1252
38+
if (\preg_match('/[\x80-\x9F]/', $input)) {
39+
return true;
40+
}
41+
42+
// Heuristic: convert the bytes as CP1252 -> UTF-8 and check how much
43+
// of the result is printable (letters, numbers, punctuation).
44+
// A high printable ratio (>= 0.6) indicates a likely readable
45+
// Win1252 interpretation.
46+
// Perform a strict conversion: do not ignore unconvertible bytes.
47+
// If conversion fails or produces an empty result, treat as not 1252.
48+
$utf = false;
49+
if (\function_exists('iconv')) {
50+
$utf = @\iconv('CP1252', 'UTF-8', $input);
51+
}
52+
if (empty($utf)) {
53+
$utf = Parser::cp1252ToUtf8($input);
54+
if (empty($utf)) {
55+
return false;
56+
}
57+
}
58+
59+
$printableCount = preg_match_all('/[\p{L}\p{N}\p{P}]/u', $utf);
60+
61+
if (extension_loaded('mbstring')) {
62+
$charCount = mb_strlen($utf, 'UTF-8');
63+
} else {
64+
preg_match_all('/./us', $utf, $chars);
65+
$charCount = count($chars[0]);
66+
}
67+
68+
if ($charCount > 0) {
69+
$ratio = $printableCount / $charCount;
70+
if ($ratio >= 0.6) {
71+
return true;
72+
}
73+
}
74+
75+
return false;
76+
}
77+
}

‎src/Utf8/Parser.php‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,15 @@ public static function parse(string $raw): array
139139
return $chars;
140140
}
141141

142+
public static function cp1252ToUtf8(string $raw): string
143+
{
144+
return str_replace(
145+
array_map('chr', array_keys(self::$winc1umap)),
146+
array_map('self::cpToUtf8Char', array_values(self::$winc1umap)),
147+
$raw,
148+
);
149+
}
150+
142151
/**
143152
* @return int|false
144153
*/

‎src/Win1252String.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace StringObject;
4+
5+
class Win1252String extends AsciiString
6+
{
7+
// This class is a placeholder for handling Windows-1252 encoded strings.
8+
// It can be extended in the future to include specific methods for encoding/decoding,
9+
// character mapping, and other functionalities related to Windows-1252.
10+
}

0 commit comments

Comments
 (0)