Skip to content

Commit b290b45

Browse files
authoredOct 29, 2024··
Merge pull request #13 from Nejcc/dev
Dev
2 parents d3e22bd + 292af7b commit b290b45

File tree

10 files changed

+771
-9
lines changed

10 files changed

+771
-9
lines changed
 

‎composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"require": {
1919
"php": "^8.2",
2020
"ext-bcmath": "*",
21-
"ext-ctype": "*"
21+
"ext-ctype": "*",
22+
"ext-zlib": "*"
2223
},
2324
"require-dev": {
2425
"phpunit/phpunit": "^11.4.2"

‎composer.lock

+9-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎examples/json.php

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<?php
2+
// json.php
3+
4+
declare(strict_types=1);
5+
6+
// Include Composer's autoloader
7+
use Nejcc\PhpDatatypes\Composite\Json;
8+
use Nejcc\PhpDatatypes\Encoding\HuffmanEncoding;
9+
10+
include_once __DIR__ . '/../vendor/autoload.php';
11+
12+
// Sample JSON data
13+
$jsonData1 = '{"users":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]}';
14+
$jsonData2 = '{"users":[{"id":3,"name":"Charlie"},{"id":4,"name":"Diana"}]}';
15+
16+
// Initialize variables for outputs
17+
$examples = [];
18+
$errorMessage = '';
19+
20+
// Start output buffering to capture print_r outputs
21+
ob_start();
22+
23+
try {
24+
// 1. Create Json instances
25+
$json1 = new Json($jsonData1);
26+
$json2 = new Json($jsonData2);
27+
$examples[] = [
28+
'title' => 'Create Json Instances',
29+
'description' => 'We create two <code>Json</code> objects with different user data.',
30+
'code' => "\$json1 = new Json(\$jsonData1);\n\$json2 = new Json(\$jsonData2);",
31+
'output' => "Json1: " . $json1->getJson() . "\nJson2: " . $json2->getJson(),
32+
];
33+
34+
// // 2. Compare Json instances
35+
// $areEqual = $json1->compareWith($json2) ? 'Yes' : 'No';
36+
// $examples[] = [
37+
// 'title' => 'Compare Json Instances',
38+
// 'description' => 'We compare <code>json1</code> and <code>json2</code> to check if they are identical.',
39+
// 'code' => "\$areEqual = \$json1->compareWith(\$json2) ? 'Yes' : 'No';",
40+
// 'output' => "Are Json1 and Json2 identical? " . $areEqual,
41+
// ];
42+
43+
// 3. Serialize Json to Array
44+
$array1 = $json1->toArray();
45+
$examples[] = [
46+
'title' => 'Serialize Json1 to Array',
47+
'description' => 'We convert <code>json1</code> to a PHP array.',
48+
'code' => "\$array1 = \$json1->toArray();",
49+
'output' => print_r($array1, true),
50+
];
51+
52+
// 4. Deserialize Array to Json
53+
$jsonFromArray = Json::fromArray($array1);
54+
$examples[] = [
55+
'title' => 'Deserialize Array to Json',
56+
'description' => 'We create a new <code>Json</code> object from <code>array1</code>.',
57+
'code' => "\$jsonFromArray = Json::fromArray(\$array1);",
58+
'output' => "Json from Array: " . $jsonFromArray->getJson(),
59+
];
60+
61+
// // 5. Compress Json1 using HuffmanEncoding
62+
$huffmanEncoder = new HuffmanEncoding();
63+
$compressed = $json1->compress($huffmanEncoder);
64+
$examples[] = [
65+
'title' => 'Compress Json1 using HuffmanEncoding',
66+
'description' => 'We compress <code>json1</code> using <code>HuffmanEncoding</code>.',
67+
'code' => "\$huffmanEncoder = new HuffmanEncoding();\n\$compressed = \$json1->compress(\$huffmanEncoder);",
68+
'output' => "Compressed Json1 (hex): " . bin2hex($compressed),
69+
];
70+
71+
// // 6. Decompress the previously compressed data
72+
$decompressedJson = $json1->decompress($huffmanEncoder, $compressed);
73+
$examples[] = [
74+
'title' => 'Decompress the Compressed Data',
75+
'description' => 'We decompress the previously compressed data to retrieve the original JSON.',
76+
'code' => "\$decompressedJson = \$json1->decompress(\$huffmanEncoder, \$compressed);",
77+
'output' => "Decompressed Json: " . $decompressedJson->getJson(),
78+
];
79+
80+
// 7. Verify decompressed data matches original
81+
$isMatch = ($json1->toArray() === $decompressedJson->toArray()) ? 'Yes' : 'No';
82+
$examples[] = [
83+
'title' => 'Verify Decompressed Data',
84+
'description' => 'We check if the decompressed JSON matches the original <code>json1</code> data.',
85+
'code' => "\$isMatch = (\$json1->toArray() === \$decompressedJson->toArray()) ? 'Yes' : 'No';",
86+
'output' => "Does decompressed Json match original Json1? " . $isMatch,
87+
];
88+
//
89+
// 8. Update Json1 by adding a new user
90+
$updatedJson1 = $json1->update('users', array_merge($json1->toArray()['users'], [['id' => 5, 'name' => 'Eve']]));
91+
$examples[] = [
92+
'title' => 'Update Json1 by Adding a New User',
93+
'description' => 'We add a new user to <code>json1</code>.',
94+
'code' => "\$updatedJson1 = \$json1->update('users', array_merge(\$json1->toArray()['users'], [['id' => 5, 'name' => 'Eve']]));",
95+
'output' => "Updated Json1: " . $updatedJson1->getJson(),
96+
];
97+
//
98+
// 9. Remove a user from updated Json1
99+
$modifiedJson1 = $updatedJson1->remove('users', 2); // Assuming remove method removes by 'id' or index
100+
$examples[] = [
101+
'title' => 'Remove a User from Updated Json1',
102+
'description' => 'We remove the user with ID 2 from <code>updatedJson1</code>.',
103+
'code' => "\$modifiedJson1 = \$updatedJson1->remove('users', 2);",
104+
'output' => "Modified Json1: " . $modifiedJson1->getJson(),
105+
];
106+
107+
} catch (InvalidArgumentException|JsonException $e) {
108+
$errorMessage = $e->getMessage();
109+
}
110+
111+
// Capture all outputs
112+
$content = ob_get_clean();
113+
114+
?>
115+
116+
<!DOCTYPE html>
117+
<html lang="en">
118+
<head>
119+
<meta charset="UTF-8">
120+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
121+
<title>Json Class Test Example</title>
122+
123+
<!-- TailwindCSS CDN for Styling -->
124+
<script src="https://cdn.tailwindcss.com"></script>
125+
126+
<!-- Prism.js for Syntax Highlighting -->
127+
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/themes/prism.min.css" rel="stylesheet"/>
128+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/prism.min.js"></script>
129+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/components/prism-php.min.js"></script>
130+
</head>
131+
<body class="bg-gray-100 p-8">
132+
<div class="max-w-7xl mx-auto">
133+
<h1 class="text-4xl font-bold text-center mb-8">Json Class Test Example</h1>
134+
135+
<?php if (!empty($errorMessage)) : ?>
136+
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-6" role="alert">
137+
<strong class="font-bold">Error:</strong>
138+
<span class="block sm:inline"><?php echo htmlspecialchars($errorMessage); ?></span>
139+
</div>
140+
<?php endif; ?>
141+
142+
<?php foreach ($examples as $example) : ?>
143+
<div class="bg-white shadow-md rounded-lg mb-6">
144+
<div class="px-6 py-4">
145+
<h2 class="text-2xl font-semibold mb-2"><?php echo htmlspecialchars($example['title']); ?></h2>
146+
<p class="text-gray-700 mb-4"><?php echo htmlspecialchars($example['description']); ?></p>
147+
148+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
149+
<!-- Code Block -->
150+
<div>
151+
<h3 class="text-xl font-medium mb-2">Code</h3>
152+
<pre class="language-php bg-gray-800 text-white p-4 rounded"><code class="language-php"><?php echo htmlspecialchars($example['code']); ?></code></pre>
153+
</div>
154+
<!-- Output Block -->
155+
<div>
156+
<h3 class="text-xl font-medium mb-2">Output</h3>
157+
<pre class="bg-gray-100 text-gray-800 p-4 rounded"><?php echo htmlspecialchars($example['output']); ?></pre>
158+
</div>
159+
</div>
160+
</div>
161+
</div>
162+
<?php endforeach; ?>
163+
</div>
164+
</body>
165+
</html>

‎src/Composite/Json.php

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Nejcc\PhpDatatypes\Composite;
6+
7+
use InvalidArgumentException;
8+
use JsonException;
9+
use Nejcc\PhpDatatypes\Interfaces\DecoderInterface;
10+
use Nejcc\PhpDatatypes\Interfaces\EncoderInterface;
11+
12+
13+
/**
14+
* Class Json
15+
* A strict and immutable type for handling JSON data with advanced features.
16+
*/
17+
final class Json
18+
{
19+
/**
20+
* @var string The JSON string.
21+
*/
22+
private readonly string $json;
23+
24+
/**
25+
* @var array|null The decoded JSON data.
26+
*/
27+
private ?array $data = null;
28+
29+
/**
30+
* @var string|null The JSON schema.
31+
*/
32+
private ?string $schema = null;
33+
34+
/**
35+
* Json constructor.
36+
*
37+
* @param string $json The JSON string.
38+
* @param string|null $schema Optional JSON schema for validation.
39+
* @throws InvalidArgumentException If the JSON is invalid or does not comply with the schema.
40+
*/
41+
public function __construct(string $json, ?string $schema = null)
42+
{
43+
$this->validateJson($json);
44+
$this->schema = $schema;
45+
$this->json = $json;
46+
}
47+
48+
/**
49+
* Validates if a string is valid JSON.
50+
*
51+
* @param string $json
52+
* @throws InvalidArgumentException
53+
*/
54+
private function validateJson(string $json): void
55+
{
56+
try {
57+
json_decode($json, true, 512, JSON_THROW_ON_ERROR);
58+
} catch (JsonException $e) {
59+
throw new InvalidArgumentException('Invalid JSON provided: ' . $e->getMessage());
60+
}
61+
}
62+
63+
64+
/**
65+
* Serializes the JSON data to an array.
66+
*
67+
* @return array
68+
* @throws JsonException
69+
*/
70+
public function toArray(): array
71+
{
72+
if ($this->data === null) {
73+
$this->data = json_decode($this->json, true, 512, JSON_THROW_ON_ERROR);
74+
}
75+
76+
return $this->data;
77+
}
78+
79+
/**
80+
* Serializes the JSON data to an object.
81+
*
82+
* @return object
83+
* @throws JsonException
84+
*/
85+
public function toObject(): object
86+
{
87+
return json_decode($this->json, false, 512, JSON_THROW_ON_ERROR);
88+
}
89+
90+
/**
91+
* Deserializes an array to a Json instance.
92+
*
93+
* @param array $data
94+
* @param string|null $schema
95+
* @return self
96+
* @throws InvalidArgumentException
97+
* @throws JsonException
98+
*/
99+
public static function fromArray(array $data, ?string $schema = null): self
100+
{
101+
$json = json_encode($data, JSON_THROW_ON_ERROR);
102+
return new self($json, $schema);
103+
}
104+
105+
/**
106+
* Deserializes an object to a Json instance.
107+
*
108+
* @param object $object
109+
* @param string|null $schema
110+
* @return self
111+
* @throws InvalidArgumentException
112+
* @throws JsonException
113+
*/
114+
public static function fromObject(object $object, ?string $schema = null): self
115+
{
116+
$json = json_encode($object, JSON_THROW_ON_ERROR);
117+
return new self($json, $schema);
118+
}
119+
120+
/**
121+
* Gets the JSON string.
122+
*
123+
* @return string
124+
*/
125+
public function getJson(): string
126+
{
127+
return $this->json;
128+
}
129+
130+
/**
131+
* Compresses the JSON string using the provided encoder.
132+
*
133+
* @param EncoderInterface $encoder
134+
* @return string The compressed string.
135+
*/
136+
public function compress(EncoderInterface $encoder): string
137+
{
138+
return $encoder->encode($this->json);
139+
}
140+
141+
/**
142+
* Decompresses the string using the provided decoder.
143+
*
144+
* @param DecoderInterface $decoder
145+
* @param string $compressed
146+
* @return self
147+
* @throws InvalidArgumentException
148+
*/
149+
public static function decompress(DecoderInterface $decoder, string $compressed): self
150+
{
151+
$json = $decoder->decode($compressed);
152+
return new self($json);
153+
}
154+
155+
/**
156+
* Merges this JSON with another Json instance.
157+
* In case of conflicting keys, values from the other Json take precedence.
158+
*
159+
* @param Json $other
160+
* @return self
161+
* @throws JsonException
162+
*/
163+
public function merge(Json $other): self
164+
{
165+
$mergedData = array_merge_recursive($this->toArray(), $other->toArray());
166+
$mergedJson = json_encode($mergedData, JSON_THROW_ON_ERROR);
167+
return new self($mergedJson, $this->schema);
168+
}
169+
170+
/**
171+
* Updates the JSON data with a given key-value pair.
172+
*
173+
* @param string $key
174+
* @param mixed $value
175+
* @return self
176+
* @throws JsonException
177+
*/
178+
public function update(string $key, mixed $value): self
179+
{
180+
$data = $this->toArray();
181+
$data[$key] = $value;
182+
$updatedJson = json_encode($data, JSON_THROW_ON_ERROR);
183+
return new self($updatedJson, $this->schema);
184+
}
185+
186+
/**
187+
* Removes a key from the JSON data.
188+
*
189+
* @param string $key
190+
* @return self
191+
* @throws JsonException
192+
*/
193+
public function remove(string $key): self
194+
{
195+
$data = $this->toArray();
196+
unset($data[$key]);
197+
$updatedJson = json_encode($data, JSON_THROW_ON_ERROR);
198+
return new self($updatedJson, $this->schema);
199+
}
200+
}

‎src/Encoding/Base64Encoding.php

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Nejcc\PhpDatatypes\Encoding;
6+
7+
use Nejcc\PhpDatatypes\Interfaces\DecoderInterface;
8+
use Nejcc\PhpDatatypes\Interfaces\EncoderInterface;
9+
10+
/**
11+
* Class Base64Encoding
12+
* Implements Base64 encoding.
13+
*/
14+
class Base64Encoding implements EncoderInterface, DecoderInterface
15+
{
16+
/**
17+
* Encodes the data using Base64.
18+
*
19+
* @param string $data
20+
* @return string
21+
*/
22+
public function encode(string $data): string
23+
{
24+
return base64_encode($data);
25+
}
26+
27+
/**
28+
* Decodes the data using Base64.
29+
*
30+
* @param string $data
31+
* @return string
32+
*/
33+
public function decode(string $data): string
34+
{
35+
$decoded = base64_decode($data, true);
36+
if ($decoded === false) {
37+
throw new \InvalidArgumentException('Base64 decoding failed.');
38+
}
39+
return $decoded;
40+
}
41+
}

‎src/Encoding/GzipEncoding.php

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Nejcc\PhpDatatypes\Encoding;
6+
7+
use Nejcc\PhpDatatypes\Interfaces\DecoderInterface;
8+
use Nejcc\PhpDatatypes\Interfaces\EncoderInterface;
9+
10+
/**
11+
* Class GzipEncoding
12+
* Implements Gzip compression.
13+
*/
14+
class GzipEncoding implements EncoderInterface, DecoderInterface
15+
{
16+
/**
17+
* Encodes the data using Gzip.
18+
*
19+
* @param string $data
20+
* @return string
21+
*/
22+
public function encode(string $data): string
23+
{
24+
$compressed = gzencode($data, 9);
25+
if ($compressed === false) {
26+
throw new \RuntimeException('Gzip encoding failed.');
27+
}
28+
return $compressed;
29+
}
30+
31+
/**
32+
* Decodes the data using Gzip.
33+
*
34+
* @param string $data
35+
* @return string
36+
*/
37+
public function decode(string $data): string
38+
{
39+
$decoded = gzdecode($data);
40+
if ($decoded === false) {
41+
throw new \InvalidArgumentException('Gzip decoding failed.');
42+
}
43+
return $decoded;
44+
}
45+
}

‎src/Encoding/HuffmanEncoding.php

+233
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
6+
namespace Nejcc\PhpDatatypes\Encoding;
7+
8+
use Nejcc\PhpDatatypes\Interfaces\DecoderInterface;
9+
use Nejcc\PhpDatatypes\Interfaces\EncoderInterface;
10+
use InvalidArgumentException;
11+
12+
/**
13+
* Class HuffmanEncoding
14+
* Implements Huffman compression and decompression.
15+
*/
16+
class HuffmanEncoding implements EncoderInterface, DecoderInterface
17+
{
18+
/**
19+
* Encodes the data using Huffman encoding.
20+
*
21+
* @param string $data
22+
* @return string The encoded data with serialized frequency table.
23+
*/
24+
public function encode(string $data): string
25+
{
26+
if ($data === '') {
27+
throw new InvalidArgumentException('Cannot encode empty string.');
28+
}
29+
30+
// Step 1: Build frequency table
31+
$frequency = $this->buildFrequencyTable($data);
32+
33+
// Step 2: Build Huffman Tree
34+
$huffmanTree = $this->buildHuffmanTree($frequency);
35+
36+
// Step 3: Generate Huffman Codes
37+
$codes = [];
38+
$this->generateCodes($huffmanTree, '', $codes);
39+
40+
// Step 4: Encode data
41+
$encodedData = '';
42+
for ($i = 0, $len = strlen($data); $i < $len; $i++) {
43+
$char = $data[$i];
44+
$encodedData .= $codes[$char];
45+
}
46+
47+
// Step 5: Serialize frequency table and encoded data
48+
// Prefix the encoded data with the JSON-encoded frequency table and a separator
49+
$serializedFrequency = json_encode($frequency);
50+
if ($serializedFrequency === false) {
51+
throw new InvalidArgumentException('Failed to serialize frequency table.');
52+
}
53+
54+
// Use a unique separator (null byte) to split frequency table and encoded data
55+
$separator = "\0";
56+
57+
// Convert bit string to byte string
58+
$byteString = $this->bitsToBytes($encodedData);
59+
60+
return $serializedFrequency . $separator . $byteString;
61+
}
62+
63+
/**
64+
* Decodes the data using Huffman decoding.
65+
*
66+
* @param string $data The encoded data with serialized frequency table.
67+
* @return string The original decoded data.
68+
*/
69+
public function decode(string $data): string
70+
{
71+
if ($data === '') {
72+
throw new InvalidArgumentException('Cannot decode empty string.');
73+
}
74+
75+
// Step 1: Split the frequency table and the encoded data
76+
$separatorPos = strpos($data, "\0");
77+
if ($separatorPos === false) {
78+
throw new InvalidArgumentException('Invalid encoded data format.');
79+
}
80+
81+
$serializedFrequency = substr($data, 0, $separatorPos);
82+
$encodedDataBytes = substr($data, $separatorPos + 1);
83+
84+
// Step 2: Deserialize frequency table
85+
$frequency = json_decode($serializedFrequency, true);
86+
if (!is_array($frequency)) {
87+
throw new InvalidArgumentException('Failed to deserialize frequency table.');
88+
}
89+
90+
// Step 3: Rebuild Huffman Tree
91+
$huffmanTree = $this->buildHuffmanTree($frequency);
92+
93+
// Step 4: Convert bytes back to bit string
94+
$encodedDataBits = $this->bytesToBits($encodedDataBytes);
95+
96+
// Step 5: Decode bit string using Huffman Tree
97+
$decodedData = '';
98+
$currentNode = $huffmanTree;
99+
$totalBits = strlen($encodedDataBits);
100+
for ($i = 0; $i < $totalBits; $i++) {
101+
$bit = $encodedDataBits[$i];
102+
if ($bit === '0') {
103+
$currentNode = $currentNode->left;
104+
} else {
105+
$currentNode = $currentNode->right;
106+
}
107+
108+
if ($currentNode->isLeaf()) {
109+
$decodedData .= $currentNode->character;
110+
$currentNode = $huffmanTree;
111+
}
112+
}
113+
114+
return $decodedData;
115+
}
116+
117+
/**
118+
* Builds a frequency table for the given data.
119+
*
120+
* @param string $data
121+
* @return array Associative array with characters as keys and frequencies as values.
122+
*/
123+
private function buildFrequencyTable(string $data): array
124+
{
125+
$frequency = [];
126+
for ($i = 0, $len = strlen($data); $i < $len; $i++) {
127+
$char = $data[$i];
128+
if (isset($frequency[$char])) {
129+
$frequency[$char]++;
130+
} else {
131+
$frequency[$char] = 1;
132+
}
133+
}
134+
return $frequency;
135+
}
136+
137+
/**
138+
* Builds the Huffman tree from the frequency table.
139+
*
140+
* @param array $frequency
141+
* @return Node The root of the Huffman tree.
142+
*/
143+
private function buildHuffmanTree(array $frequency): Node
144+
{
145+
// Create a priority queue (min-heap) based on frequency
146+
$pq = new \SplPriorityQueue();
147+
$pq->setExtractFlags(\SplPriorityQueue::EXTR_DATA);
148+
149+
foreach ($frequency as $char => $freq) {
150+
// Ensure $char is a string
151+
$char = (string)$char;
152+
// Since SplPriorityQueue is a max-heap, use negative frequency for min-heap behavior
153+
$pq->insert(new Node($char, $freq), -$freq);
154+
}
155+
156+
// Edge case: Only one unique character
157+
if ($pq->count() === 1) {
158+
$onlyNode = $pq->extract();
159+
return new Node((string)$onlyNode->character, $onlyNode->frequency, $onlyNode, null);
160+
}
161+
162+
// Build the Huffman tree
163+
while ($pq->count() > 1) {
164+
$left = $pq->extract();
165+
$right = $pq->extract();
166+
$merged = new Node('', $left->frequency + $right->frequency, $left, $right);
167+
$pq->insert($merged, -$merged->frequency);
168+
}
169+
170+
return $pq->extract();
171+
}
172+
173+
/**
174+
* Generates Huffman codes by traversing the tree.
175+
*
176+
* @param Node $node
177+
* @param string $prefix
178+
* @param array &$codes
179+
* @return void
180+
*/
181+
private function generateCodes(Node $node, string $prefix, array &$codes): void
182+
{
183+
if ($node->isLeaf()) {
184+
// Edge case: If there's only one unique character, assign '0' as its code
185+
$codes[$node->character] = $prefix === '' ? '0' : $prefix;
186+
return;
187+
}
188+
189+
if ($node->left !== null) {
190+
$this->generateCodes($node->left, $prefix . '0', $codes);
191+
}
192+
193+
if ($node->right !== null) {
194+
$this->generateCodes($node->right, $prefix . '1', $codes);
195+
}
196+
}
197+
198+
/**
199+
* Converts a bit string to a byte string.
200+
*
201+
* @param string $bits
202+
* @return string
203+
*/
204+
private function bitsToBytes(string $bits): string
205+
{
206+
$bytes = '';
207+
$length = strlen($bits);
208+
for ($i = 0; $i < $length; $i += 8) {
209+
$byte = substr($bits, $i, 8);
210+
if (strlen($byte) < 8) {
211+
$byte = str_pad($byte, 8, '0'); // Pad with zeros if not enough bits
212+
}
213+
$bytes .= chr(bindec($byte));
214+
}
215+
return $bytes;
216+
}
217+
218+
/**
219+
* Converts a byte string back to a bit string.
220+
*
221+
* @param string $bytes
222+
* @return string
223+
*/
224+
private function bytesToBits(string $bytes): string
225+
{
226+
$bits = '';
227+
$length = strlen($bytes);
228+
for ($i = 0; $i < $length; $i++) {
229+
$bits .= str_pad(decbin(ord($bytes[$i])), 8, '0', STR_PAD_LEFT);
230+
}
231+
return $bits;
232+
}
233+
}

‎src/Encoding/Node.php

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Nejcc\PhpDatatypes\Encoding;
6+
7+
/**
8+
* Class Node
9+
* Represents a node in the Huffman tree.
10+
*/
11+
class Node
12+
{
13+
public string $character;
14+
public int $frequency;
15+
public ?Node $left;
16+
public ?Node $right;
17+
18+
public function __construct(string $character, int $frequency, ?Node $left = null, ?Node $right = null)
19+
{
20+
$this->character = $character;
21+
$this->frequency = $frequency;
22+
$this->left = $left;
23+
$this->right = $right;
24+
}
25+
26+
/**
27+
* Check if the node is a leaf node.
28+
*
29+
* @return bool
30+
*/
31+
public function isLeaf(): bool
32+
{
33+
return is_null($this->left) && is_null($this->right);
34+
}
35+
}

‎src/Interfaces/DecoderInterface.php

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
6+
namespace Nejcc\PhpDatatypes\Interfaces;
7+
8+
/**
9+
* Interface DecoderInterface
10+
* Defines the contract for decoding strategies.
11+
*/
12+
interface DecoderInterface
13+
{
14+
/**
15+
* Decodes the given data.
16+
*
17+
* @param string $data
18+
* @return string The decoded data.
19+
*/
20+
public function decode(string $data): string;
21+
}

‎src/Interfaces/EncoderInterface.php

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Nejcc\PhpDatatypes\Interfaces;
6+
7+
/**
8+
* Interface EncoderInterface
9+
* Defines the contract for encoding strategies.
10+
*/
11+
interface EncoderInterface
12+
{
13+
/**
14+
* Encodes the given data.
15+
*
16+
* @param string $data
17+
* @return string The encoded data.
18+
*/
19+
public function encode(string $data): string;
20+
}

0 commit comments

Comments
 (0)
Please sign in to comment.