Skip to content

Commit

Permalink
Scream trait
Browse files Browse the repository at this point in the history
  • Loading branch information
fprochazka committed Mar 29, 2016
1 parent 83f95fa commit 5676ac4
Show file tree
Hide file tree
Showing 8 changed files with 484 additions and 0 deletions.
118 changes: 118 additions & 0 deletions src/Scream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

/**
* This file is part of the Kdyby (http://www.kdyby.org)
*
* Copyright (c) 2008 Filip Procházka ([email protected])
*
* For the full copyright and license information, please view the file license.txt that was distributed with this source code.
*/

namespace Kdyby\StrictObjects;

use Kdyby;
use Nette;



/**
* @author Filip Procházka <[email protected]>
* @author David Grudl <https://davidgrudl.com>
*/
trait Scream
{

/**
* Call to undefined method.
*
* @param string $name method name
* @param array $args arguments
* @throws \Kdyby\StrictObjects\MemberAccessException
*/
public function __call($name, $args)
{
$class = get_class($this);
$hint = Suggester::suggestMethod($class, $name);
throw new MemberAccessException("Call to undefined method $class::$name()" . ($hint ? ", did you mean $hint()?" : '.'));
}



/**
* Call to undefined static method.
*
* @param string $name method name (in lower case!)
* @param array $args arguments
* @throws \Kdyby\StrictObjects\MemberAccessException
*/
public static function __callStatic($name, $args)
{
$class = get_called_class();
$hint = Suggester::suggestStaticFunction($class, $name);
throw new MemberAccessException("Call to undefined static method $class::$name()" . ($hint ? ", did you mean $hint()?" : '.'));
}



/**
* Returns property value. Do not call directly.
*
* @param string $name property name
* @throws \Kdyby\StrictObjects\MemberAccessException
*/
public function &__get($name)
{
$class = get_class($this);
$hint = Suggester::suggestProperty($class, $name);
throw new MemberAccessException("Cannot read an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
}



/**
* Sets value of a property. Do not call directly.
*
* @param string $name property name
* @param mixed $value property value
* @throws \Kdyby\StrictObjects\MemberAccessException
* @return void
*/
public function __set($name, $value)
{
$class = get_class($this);
$hint = Suggester::suggestProperty($class, $name);
throw new MemberAccessException("Cannot write to an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
}



/**
* Is property defined?
*
* @param string $name property name
* @throws \Kdyby\StrictObjects\MemberAccessException
* @return bool
*/
public function __isset($name)
{
$class = get_class($this);
$hint = Suggester::suggestProperty($class, $name);
throw new MemberAccessException("Cannot write to an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
}



/**
* Access to undeclared property.
*
* @param string $name property name
* @throws \Kdyby\StrictObjects\MemberAccessException
* @return void
*/
public function __unset($name)
{
$class = get_class($this);
throw new MemberAccessException("Cannot unset the property $class::\$$name.");
}

}
100 changes: 100 additions & 0 deletions src/Suggester.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

/**
* This file is part of the Kdyby (http://www.kdyby.org)
*
* Copyright (c) 2008 Filip Procházka ([email protected])
*
* For the full copyright and license information, please view the file license.txt that was distributed with this source code.
*/

namespace Kdyby\StrictObjects;

use Kdyby;
use Nette;



/**
* @author Filip Procházka <[email protected]>
*/
final class Suggester
{

/**
* @param string $class
* @param string $method
* @return NULL|string
*/
public static function suggestMethod($class, $method)
{
return self::getSuggestion(
get_class_methods($class),
$method
);
}



/**
* @param string $class
* @param string $method
* @return NULL|string
*/
public static function suggestStaticFunction($class, $method)
{
return self::getSuggestion(
array_filter(
get_class_methods($class),
function ($m) use ($class) {
return (new \ReflectionMethod($class, $m))->isStatic();
}
),
$method
);
}



/**
* @param string $class
* @param string $name
* @return NULL|string
*/
public static function suggestProperty($class, $name)
{
return self::getSuggestion(
array_keys(get_class_vars($class)),
$name
);
}



/**
* Finds the best suggestion (for 8-bit encoding).
*
* @author David Grudl (https://davidgrudl.com)
* @license See https://nette.org/en/license
* @return string|NULL
* @internal
*/
public static function getSuggestion(array $items, $value)
{
$norm = preg_replace($re = '#^(get|set|has|is|add)(?=[A-Z])#', '', $value);
$best = NULL;
$min = (strlen($value) / 4 + 1) * 10 + .1;
foreach (array_unique($items) as $item) {
if ($item !== $value && (
($len = levenshtein($item, $value, 10, 11, 10)) < $min
|| ($len = levenshtein(preg_replace($re, '', $item), $norm, 10, 11, 10) + 20) < $min
)
) {
$min = $len;
$best = $item;
}
}
return $best;
}

}
23 changes: 23 additions & 0 deletions src/exceptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/**
* This file is part of the Kdyby (http://www.kdyby.org)
*
* Copyright (c) 2008 Filip Procházka ([email protected])
*
* For the full copyright and license information, please view the file license.txt that was distributed with this source code.
*/

namespace Kdyby\StrictObjects;

interface Exception
{

}



class MemberAccessException extends \LogicException implements Exception
{

}
3 changes: 3 additions & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
coverage.dat
*.actual
*.expected
89 changes: 89 additions & 0 deletions tests/Scream.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

/**
* @testCase
*/

namespace KdybyTests\Doctrine\MagicAccessors;

use KdybyTests\StrictObjects\SomeObject;
use Tester;
use Tester\Assert;

require_once __DIR__ . '/bootstrap.php';



/**
* @author Filip Procházka <[email protected]>
*/
class ScreamTest extends Tester\TestCase
{

public function testMagicCall()
{
$o = new SomeObject();

Assert::exception(function () use ($o) {
$o->someBaZ();
}, 'Kdyby\StrictObjects\MemberAccessException', 'Call to undefined method KdybyTests\StrictObjects\SomeObject::someBaZ(), did you mean someBar()?');
}



public function testMagicStaticCall()
{
Assert::exception(function () {
SomeObject::staBaz();
}, 'Kdyby\StrictObjects\MemberAccessException', 'Call to undefined static method KdybyTests\StrictObjects\SomeObject::staBaz(), did you mean staBar()?');
}



public function testMagicGet()
{
$o = new SomeObject();

Assert::exception(function () use ($o) {
$o->baz;
}, 'Kdyby\StrictObjects\MemberAccessException', 'Cannot read an undeclared property KdybyTests\StrictObjects\SomeObject::$baz, did you mean $bar?');
}



public function testMagicSet()
{
$o = new SomeObject();

Assert::exception(function () use ($o) {
$o->baz = 'value';
}, 'Kdyby\StrictObjects\MemberAccessException', 'Cannot write to an undeclared property KdybyTests\StrictObjects\SomeObject::$baz, did you mean $bar?');
}



public function testMagicUnset()
{
$o = new SomeObject();

Assert::exception(function () use ($o) {
unset($o->baz);
}, 'Kdyby\StrictObjects\MemberAccessException', 'Cannot unset the property KdybyTests\StrictObjects\SomeObject::$baz.');
}



public function testMagicIsset()
{
$o = new SomeObject();

Assert::exception(function () use ($o) {
isset($o->baz);
}, 'Kdyby\StrictObjects\MemberAccessException', 'Cannot write to an undeclared property KdybyTests\StrictObjects\SomeObject::$baz, did you mean $bar?');
}

}



(new ScreamTest())->run();
Loading

0 comments on commit 5676ac4

Please sign in to comment.