Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Php service, PHP 7.2 support #22

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions benchmarks/Base58PHPEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace StephenHill\Benchmarks;

use Athletic\AthleticEvent;
use StephenHill\Base58;
use StephenHill\PHPService;

class Base58PHPEvent extends AthleticEvent
{
protected $base58;

public function setUp()
{
$this->base58 = new Base58(null, new PHPService());
}

/**
* @iterations 10000
*/
public function encodeBase58()
{
$this->base58->encode('Hello World');
}

/**
* @iterations 10000
*/
public function decodeBase58()
{
$this->base58->decode('JxF12TrwUP45BMd');
}
}
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"name": "stephenhill/base58",
"description": "Base58 Encoding and Decoding Library for PHP",
"require-dev": {
"phpunit/phpunit": "4.*",
"athletic/athletic": "~0.1"
"athletic/athletic": "~0.1",
"phpunit/phpunit": "^7.4"
},
"license": "MIT",
"authors": [
Expand All @@ -21,4 +21,4 @@
"StephenHill\\Benchmarks\\": "benchmarks/"
}
}
}
}
1 change: 0 additions & 1 deletion phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
convertWarningsToExceptions="true"
processIsolation="true"
stopOnFailure="false"
syntaxCheck="true"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
verbose="true"
Expand Down
2 changes: 1 addition & 1 deletion src/BCMathService.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public function encode($string)
$output = '';
while ($decimal >= $this->base) {
$div = bcdiv($decimal, $this->base, 0);
$mod = bcmod($decimal, $this->base);
$mod = (int) bcmod($decimal, $this->base);
$output .= $this->alphabet[$mod];
$decimal = $div;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Base58.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function __construct(
$service = new BCMathService($alphabet);
}
else {
throw new \Exception('Please install the BC Math or GMP extension.');
$service = new PHPService($alphabet);
}
}

Expand Down
190 changes: 190 additions & 0 deletions src/PHPService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<?php

namespace StephenHill;

use InvalidArgumentException;

class PHPService implements ServiceInterface
{
/**
* @var string
* @since v1.1.0
*/
protected $alphabet;

/**
* @var int
* @since v1.1.0
*/
protected $base;

/**
* Constructor
*
* @param string $alphabet optional
* @since v1.1.0
*/
public function __construct($alphabet = null)
{
// Handle null alphabet
if ($alphabet === null) {
$alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
}

// Type validation
if (\is_string($alphabet) === false) {
throw new InvalidArgumentException('Argument $alphabet must be a string.');
}

// The alphabet must contain 58 characters
if (\strlen($alphabet) !== 58) {
throw new InvalidArgumentException('Argument $alphabet must contain 58 characters.');
}

$this->alphabet = $alphabet;
$this->base = \strlen($alphabet);
}
/**
* Encode a string into base58.
*
* @param string $string The string you wish to encode.
* @since Release v1.1.0
* @return string The Base58 encoded string.
*/
public function encode($string): string
{
// Type validation
if (\is_string($string) === false) {
throw new InvalidArgumentException('Argument $string must be a string.');
}

// If the string is empty, then the encoded string is obviously empty
if ($string === '') {
return '';
}

// Strings in PHP are essentially 8-bit byte arrays
// so lets convert the string into a PHP array
$bytes = array_values(unpack('C*', $string));

$leadingZerosNeeded = 0;
foreach ($bytes as $byte) {
if ($byte !== 0) {
break;
}

$leadingZerosNeeded++;
}

$source = \array_slice($bytes, $leadingZerosNeeded);
$result = $this->convertBase($source, 256, 58);

// Count existing leading zeros
$leadingZeroCount = 0;
foreach ($result as $digit) {
if ($digit !== 0) {
break;
}

$leadingZeroCount++;
}

// Now we need to add any missing leading zeros
for ($i = $leadingZeroCount; $i < $leadingZerosNeeded; $i++) {
array_unshift($result, 0);
}

// Encode to a string
return implode('', array_map(function ($ord) { return $this->alphabet[$ord]; }, $result));
}

/**
* Decode base58 into a PHP string.
*
* @param string $base58 The base58 encoded string.
* @since Release v1.1.0
* @return string Returns the decoded string.
*/
public function decode($base58): string
{
// Type Validation
if (\is_string($base58) === false) {
throw new InvalidArgumentException('Argument $base58 must be a string.');
}

// If the string is empty, then the decoded string is obviously empty
if ($base58 === '') {
return '';
}

$indexes = array_flip(str_split($this->alphabet));
$chars = str_split($base58);
$digits = [];

// Check for invalid characters in the supplied base58 string
foreach ($chars as $char) {
if (isset($indexes[$char]) === false) {
throw new InvalidArgumentException('Argument $base58 contains invalid characters. ($char: "'.$char.'" | $base58: "'.$base58.'") ');
}

$digits[] = $indexes[$char];
}

$leadingZerosNeeded = 0;
foreach ($digits as $digit) {
if ($digit !== 0) {
break;
}

$leadingZerosNeeded++;
}

$source = \array_slice($digits, $leadingZerosNeeded);
$result = $this->convertBase($source, 58, 256);

// Count existing leading zeros
$leadingZeroCount = 0;
foreach ($result as $digit) {
if ($digit !== 0) {
break;
}

$leadingZeroCount++;
}

// Now we need to add any missing leading zeros
for ($i = $leadingZeroCount; $i < $leadingZerosNeeded; $i++) {
array_unshift($result, 0);
}

// Encode to a string
return implode('', array_map('\chr', $result));
}

/**
* Basic manual base conversion algorithm,
* @see https://en.wikipedia.org/wiki/Positional_notation#Base_conversion
**/
private function convertBase(array $digits, int $base1, int $base2): array
{
$result = [];

do {
$digitCount = \count($digits);
$quotient = [];
$remainder = 0;

for ($i = 0; $i < $digitCount; $i++) {
$dividend = $remainder * $base1 + $digits[$i];
$quotient[] = intdiv($dividend, $base2);
$remainder = $dividend % $base2;
}

$result[] = $remainder;
$digits = $quotient;
} while (array_sum($quotient) > 0);

// Now we need to reverse the results
return array_reverse($result);
}
}
7 changes: 5 additions & 2 deletions tests/Base58Test.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<?php

use PHPUnit\Framework\TestCase;
use StephenHill\Base58;
use StephenHill\BCMathService;
use StephenHill\GMPService;
use StephenHill\PHPService;

class Base58Tests extends PHPUnit_Framework_TestCase
class Base58Tests extends TestCase
{
/**
* @dataProvider encodingsProvider
Expand Down Expand Up @@ -32,7 +34,8 @@ public function encodingsProvider()
{
$instances = array(
new Base58(null, new BCMathService()),
new Base58(null, new GMPService())
new Base58(null, new GMPService()),
new Base58(null, new PHPService()),
);

$tests = array(
Expand Down