Skip to content

Commit

Permalink
First version with Json class
Browse files Browse the repository at this point in the history
  • Loading branch information
otsch committed May 17, 2023
1 parent 12ef53e commit 92c677b
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

return $config->setFinder($finder)
->setRules([
'@PSR12' => true,
'@PER' => true,
'strict_param' => true,
'single_class_element_per_statement' => false,
])
Expand Down
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.1.0] - 20xx-xx-xx
### Added (Changed, Fixed, Remove)
- foo bar
## [1.0.0] - 2023-05-17
### Added
- The `Json` class that converts JSON strings to array and also tries to deal with "relaxed" JSON, where keys can be unquoted.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ this package. So first off: Awesome! 👍🤘
## Bugs

In case you encounter any bugs please
[file an issue](https://github.com/crwlrsoft/.../issues/new).
[file an issue](https://github.com/crwlrsoft/utils/issues/new).
Describe the issue as well as you can and provide an example to
reproduce it.
Maybe you're not 100 percent sure whether what you've discovered
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2022 Christian Olear
Copyright (c) 2023 Christian Olear

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
<p align="center"><a href="https://www.crwlr.software" target="_blank"><img src="https://github.com/crwlrsoft/graphics/blob/eee6cf48ee491b538d11b9acd7ee71fbcdbe3a09/crwlr-logo.png" alt="crwlr.software logo" width="260"></a></p>

# Crwlr Package Template
# Crwlr Utils

This is the starting point for new crwlr packages. After checkout, you should at least customize the following things:

- `composer.json`: name, description, keywords, homepage, authors, support (issues, source, docs), autoload path.
- Adapt URLs to file issues in `CONTRIBUTING.md`.
- Remove `ExampleClass` and `ExampleTest` from `src` and `test` and start adding your actual code instead.
- Rewrite the content of this file.
This package contains utilities that are needed in multiple crwlr packages.
14 changes: 6 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
{
"name": "crwlr/package-template",
"description": "Template repository for new crwlr packages.",
"name": "crwlr/utils",
"description": "Utilities that are needed in multiple crawler packages.",
"keywords": [
"crwlr"
"crwlr", "utils", "json"
],
"homepage": "https://www.crwlr.software/packages/...",
"type": "library",
"license": "MIT",
"authors": [
Expand All @@ -15,13 +14,12 @@
}
],
"support": {
"issues": "https://github.com/crwlrsoft/.../issues",
"source": "https://github.com/crwlrsoft/...",
"docs": "https://www.crwlr.software/packages/..."
"issues": "https://github.com/crwlrsoft/utils/issues",
"source": "https://github.com/crwlrsoft/utils"
},
"autoload": {
"psr-4": {
"Crwlr\\PackageTemplate\\": "src/"
"Crwlr\\Utils\\": "src/"
}
},
"require": {
Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
level: 8
level: 9
paths:
- src
- tests
Expand Down
11 changes: 0 additions & 11 deletions src/ExampleClass.php

This file was deleted.

9 changes: 9 additions & 0 deletions src/Exceptions/InvalidJsonException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Crwlr\Utils\Exceptions;

use Exception;

class InvalidJsonException extends Exception
{
}
65 changes: 65 additions & 0 deletions src/Json.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Crwlr\Utils;

use Crwlr\Utils\Exceptions\InvalidJsonException;

class Json
{
/**
* @param string $string
* @return mixed[]
* @throws InvalidJsonException
*/
public static function stringToArray(string $string): array
{
$array = json_decode($string, true);

if (!is_array($array)) {
$array = json_decode(self::fixJsonString($string), true);

if (!is_array($array)) {
throw new InvalidJsonException('Failed to decode JSON string.');
}
}

return $array;
}

/**
* Try to fix JSON keys without quotes
*
* PHPs json_decode() doesn't work with JSON objects where the keys are not wrapped in quotes.
* This method tries to fix this, when json_decode() fails to parse a JSON string.
*/
protected static function fixJsonString(string $jsonString): string
{
return preg_replace_callback(
'/(?:(\w+):(\s*".*?"\s*(?:,|}))|(\w+):(\s*[^"]+?\s*(?:,|})))/i',
function ($match) {
if (count($match) === 3) {
$key = $match[1];

$value = $match[2];
} elseif (count($match) === 5) {
$key = $match[3];

$value = $match[4];
} else {
return $match[0];
}

if (!str_starts_with($key, '"')) {
$key = '"' . $key;
}

if (!str_ends_with($key, '"')) {
$key = $key . '"';
}

return $key . ':' . $value;
},
$jsonString
) ?? $jsonString;
}
}
7 changes: 0 additions & 7 deletions tests/ExampleTest.php

This file was deleted.

66 changes: 66 additions & 0 deletions tests/JsonTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

use Crwlr\Utils\Exceptions\InvalidJsonException;
use Crwlr\Utils\Json;

it('converts a valid JSON string to an array', function () {
$jsonString = <<<JSON
{
"foo": "one",
"bar": 2,
"baz": ["three", true, 5.1]
}
JSON;

expect(Json::stringToArray($jsonString))->toBe([
'foo' => 'one',
'bar' => 2,
'baz' => ['three', true, 5.1],
]);
});

it('works with JS style JSON objects without quotes around keys', function () {
$jsonString = <<<JSON
{
foo: "one",
bar: "two",
"baz": "three"
}
JSON;

expect(Json::stringToArray($jsonString))->toBe(['foo' => 'one', 'bar' => 'two', 'baz' => 'three']);
});

it('correctly fixes keys without quotes, even when values contain colons', function () {
$jsonString = <<<JSON
{
foo: "https://www.example.com",
bar: 2,
"baz": "some: thing"
}
JSON;

expect(Json::stringToArray($jsonString))->toBe([
'foo' => 'https://www.example.com',
'bar' => 2,
'baz' => 'some: thing',
]);
});

it('correctly fixes keys without quotes, when the value is an empty string', function () {
$jsonString = <<<JSON
{
foo: "",
"bar": "baz"
}
JSON;

expect(Json::stringToArray($jsonString))->toBe([
'foo' => '',
'bar' => 'baz',
]);
});

it('throws an exception when the string is not a (valid) JSON string', function () {
Json::stringToArray('{ foo: bar ]');
})->throws(InvalidJsonException::class);

0 comments on commit 92c677b

Please sign in to comment.