Browse Source

Initial commit

main v1.0.0
Dustin Wilson 1 year ago
commit
7d9c8801ad
  1. 77
      .gitignore
  2. 4
      AUTHORS
  3. 22
      LICENSE
  4. 7
      README.md
  5. 22
      composer.json
  6. 1749
      composer.lock
  7. 55
      lib/GettersAndSetters.php
  8. 100
      run
  9. 23
      tests/bootstrap.php
  10. 155
      tests/cases/TestGettersAndSetters.php
  11. 23
      tests/phpunit.dist.xml

77
.gitignore

@ -0,0 +1,77 @@
# Project-specific
/test*.html
/test*.php
/test/
/test/*
# General
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
/vendor/
/vendor-bin/*/vendor
/tests/html5lib-tests
/tests/.phpunit.result.cache
/tests/coverage
cachegrind.out.*

4
AUTHORS

@ -0,0 +1,4 @@
Project leads
-------------
Dustin Wilson https://dustinwilson.com/
J. King https://jkingweb.ca/

22
LICENSE

@ -0,0 +1,22 @@
Copyright (c) 2023 Dustin Wilson, J. King
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

7
README.md

@ -0,0 +1,7 @@
## Getters & Setters ##
Let's face it. Getters and setters in PHP sucks. Instead of having getter and setter accessor methods for classes we instead have the `__get` and `__set` magic methods to handle all properties. Not only are they unwieldy to use when you have many properties they also become difficult to handle when inheriting, especially when traits are involved. This trait attempts to create hackish getter and setter methods that can be extended by simple inheritance.
This trait was originally called MagicProperties and was available in the `mensbeam/framework` package. That one has been discontinued.
This is a stub. Documentation is forthcoming.

22
composer.json

@ -0,0 +1,22 @@
{
"name": "mensbeam/magic-properties",
"description": "Getter and setter method trait for PHP",
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {
"MensBeam\\Foundation\\": "lib/"
}
},
"authors": [
{
"name": "Dustin Wilson",
"email": "dustin@dustinwilson.com"
}
],
"require": {},
"require-dev": {
"phpunit/phpunit": "^9.5",
"nikic/php-parser": "^4.15"
}
}

1749
composer.lock

File diff suppressed because it is too large

55
lib/GettersAndSetters.php

@ -0,0 +1,55 @@
<?php
/**
* @license MIT
* Copyright 2023 Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
namespace MensBeam\Foundation;
trait GettersAndSetters {
public function __get(string $name): mixed {
$methodName = $this->__getGetterSetterMethodName($name);
if ($methodName === null) {
trigger_error(sprintf('Undefined property: %s::%s', get_called_class(), $name), \E_USER_WARNING);
return null;
}
return $this->$methodName();
}
public function __isset(string $name): bool {
return ($this->__getGetterSetterMethodName($name) !== null);
}
public function __set(string $name, $value): void {
$methodName = $this->__getGetterSetterMethodName($name, false);
if ($methodName !== null) {
$this->$methodName($value);
return;
}
$calledClass = get_called_class();
if ($this->__getGetterSetterMethodName($name) !== null) {
throw new \Error(sprintf('Cannot modify readonly property %s::%s', $calledClass, $name));
}
trigger_error(sprintf('Undefined property: %s::%s', $calledClass, $name), \E_USER_WARNING);
}
public function __unset(string $name): void {
$methodName = $this->__getGetterSetterMethodName($name, false);
if ($methodName === null) {
throw new \Error(sprintf('Cannot modify readonly property %s::%s', get_called_class(), $name));
}
call_user_func([ $this, $methodName ], null);
}
private function __getGetterSetterMethodName(string $name, bool $get = true): ?string {
$methodName = "__" . (($get) ? 'get' : 'set') . "_{$name}";
return (method_exists($this, $methodName)) ? $methodName : null;
}
}

100
run

@ -0,0 +1,100 @@
#!/usr/bin/env php
<?php
$cwd = __DIR__;
$codeDir = "$cwd/lib";
$testDir = "$cwd/tests";
function help($error = true): void {
$help = <<<USAGE
Usage:
run test [additional_phpunit_options]
run --help
USAGE;
if ($error) {
fprintf(\STDERR, $help);
} else {
echo $help;
}
exit((int)$error);
}
function error(string $message): void {
fprintf(\STDERR, "ERROR: $message\n");
exit(1);
}
if (count($argv) === 1) {
help();
}
switch ($argv[1]) {
case 'test':
$opts = [
'--colors',
'--coverage-html='.escapeshellarg("$testDir/coverage")
];
if (isset($argv[2])) {
$opts = [ ...$opts, array_slice($argv, 2) ];
}
$opts = implode(' ', $opts);
break;
case '-h':
case '--help':
help(false);
break;
default:
help();
}
$phpunitPath = escapeshellarg("$cwd/vendor/bin/phpunit");
$confPath = "$testDir/phpunit.dist.xml";
if (!file_exists($confPath)) {
$confPath = "$testDir/phpunit.xml";
if (!file_exists($confPath)) {
error('A phpunit configuration must be present at "tests/phpunit.dist.xml" or "tests/phpunit.xml"; aborting');
}
}
$confPath = escapeshellarg($confPath);
$cmd = [
escapeshellarg(\PHP_BINARY),
'-d opcache.enable_cli=0',
'-d zend.assertions=1'
];
if (!extension_loaded('xdebug')) {
$extDir = rtrim(ini_get("extension_dir"), "/");
if (file_exists("$extDir/xdebug.so")) {
$cmd[] = '-d zend_extension=xdebug.so';
} else {
error('Xdebug is not installed on your system; aborting');
}
}
$cmd[] = '-d xdebug.mode=coverage,develop,trace';
$cmd = implode(' ', $cmd);
$process = proc_open("$cmd $phpunitPath -c $confPath $opts", [
1 => ['pipe', 'w'],
2 => ['pipe', 'w']
], $pipes);
if ($process === false) {
error('Failed to execute phpunit');
}
$stderr = trim(stream_get_contents($pipes[2]));
$output = trim(stream_get_contents($pipes[1]));
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
echo "$output\n";
if ($stderr !== '') {
error($stderr);
}

23
tests/bootstrap.php

@ -0,0 +1,23 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace MensBeam\Foundation;
ini_set('memory_limit', '-1');
ini_set('zend.assertions', '1');
ini_set('assert.exception', 'true');
error_reporting(\E_ALL);
$cwd = dirname(__DIR__);
require_once "$cwd/vendor/autoload.php";
if (function_exists('xdebug_set_filter')) {
if (defined('XDEBUG_PATH_INCLUDE')) {
xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, \XDEBUG_PATH_INCLUDE, [ "$cwd/lib/" ]);
} else {
xdebug_set_filter(\XDEBUG_FILTER_CODE_COVERAGE, \XDEBUG_PATH_WHITELIST, [ "$cwd/lib/" ]);
}
}

155
tests/cases/TestGettersAndSetters.php

@ -0,0 +1,155 @@
<?php
/**
* @license MIT
* Copyright 2023 Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
namespace MensBeam\Foundation\TestCase;
use MensBeam\Foundation\GettersAndSetters;
/** @covers \MensBeam\Foundation\GettersAndSetters */
class TestGettersAndSetters extends \PHPUnit\Framework\TestCase {
public function provideFailures__errors(): iterable {
$ook = new class {
use GettersAndSetters;
protected ?string $_eek = 'eek';
protected ?string $_ook = 'ook';
protected function __get_eek(): ?string {
return $this->_eek;
}
protected function __get_ook(): ?string {
return $this->_ook;
}
protected function __set_ook(?string $value): void {
$this->_ook = $value;
}
};
return [
[ function() use($ook) {
$ook->eek = 'ook';
} ],
[ function() use($ook) {
unset($ook->eek);
} ]
];
}
/**
* @dataProvider provideFailures__errors
* @covers \MensBeam\Foundation\GettersAndSetters::__get
* @covers \MensBeam\Foundation\GettersAndSetters::__set
* @covers \MensBeam\Foundation\GettersAndSetters::__unset
*/
public function testFailures__errors(\Closure $closure): void {
$this->expectException(\Error::class);
$closure();
}
public function provideFailures__warnings(): iterable {
$ook = new class {
use GettersAndSetters;
protected ?string $_eek = 'eek';
protected ?string $_ook = 'ook';
protected function __get_eek(): ?string {
return $this->_eek;
}
protected function __get_ook(): ?string {
return $this->_ook;
}
protected function __set_ook(?string $value): void {
$this->_ook = $value;
}
};
return [
[ function() use($ook) {
$ook->ack;
} ],
[ function() use($ook) {
$ook->ack = 'ack';
} ]
];
}
/**
* @dataProvider provideFailures__warnings
* @covers \MensBeam\Foundation\GettersAndSetters::__get
* @covers \MensBeam\Foundation\GettersAndSetters::__set
* @covers \MensBeam\Foundation\GettersAndSetters::__unset
*/
public function testFailures__warnings(\Closure $closure): void {
$this->expectWarning();
$closure();
}
/** @covers \MensBeam\Foundation\GettersAndSetters::__isset */
public function testIsset(): void {
$ook = new class {
use GettersAndSetters;
protected function __get_ook(): ?string {
return 'ook';
}
};
$this->assertTrue(isset($ook->ook));
}
/** @covers \MensBeam\Foundation\GettersAndSetters::__unset */
public function testUnset(): void {
$ook = new class {
use GettersAndSetters;
protected ?string $_ook = 'ook';
protected function __get_ook(): ?string {
return $this->_ook;
}
protected function __set_ook(?string $value): void {
$this->_ook = $value;
}
};
unset($ook->ook);
$this->assertNull($ook->ook);
}
/** @covers \MensBeam\Foundation\GettersAndSetters::__set */
public function testSet(): void {
$ook = new class {
use GettersAndSetters;
protected ?string $_ook = 'ook';
protected function __get_ook(): ?string {
return $this->_ook;
}
protected function __set_ook(?string $value): void {
$this->_ook = $value;
}
};
$ook->ook = 'eek';
$this->assertSame('eek', $ook->ook);
}
}

23
tests/phpunit.dist.xml

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
colors="true"
bootstrap="bootstrap.php"
convertErrorsToExceptions="false"
convertNoticesToExceptions="false"
convertWarningsToExceptions="true"
beStrictAboutTestsThatDoNotTestAnything="true"
forceCoversAnnotation="true"
stopOnError="false">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">../lib</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Main">
<file>cases/TestGettersAndSetters.php</file>
</testsuite>
</testsuites>
</phpunit>
Loading…
Cancel
Save