I'm trying to test if a class is final. Since I've not found a default matcher for this (or any other clean way of testing it), I decided to create a custom extension which adds a new matcher to do just that, but I can't get it to work.
I've tried it with an inline matcher, like so:
public function getMatchers(): array
{
return [
'beFinal' => function ($subject) {
$reflection = new \ReflectionClass($subject);
if (!$reflection->isFinal()) {
throw new FailureException('Expected subject to be final, but it is not.');
}
return true;
},
];
}
This works well enough when I call $this->shouldBeFinal();. The problem is that when I call $this->shouldNotBeFinal();, it outputs a generic message: [obj:Class\To\Test] not expected to beFinal(), but it did., instead of one I'd like to show.
Another problem is that I don't want this for just one class. That's why I decided to make an extension for it.
Here's what I got:
phpspec.yml:
extensions:
PhpSpecMatchers\Extension: ~
PhpSpecMatchers/Extension.php:
<?php
declare(strict_types=1);
namespace PhpSpecMatchers;
use PhpSpec\ServiceContainer;
use PhpSpecMatchers\Matchers\BeFinalMatcher;
class Extension implements \PhpSpec\Extension
{
public function load(ServiceContainer $container, array $params): void
{
$container->define(
'php_spec_matchers.matchers.be_final',
function ($c) {
return new BeFinalMatcher();
},
['matchers']
);
}
}
PhpSpecMatchers/Matchers/BeFinalMatcher.php:
<?php
declare(strict_types=1);
namespace PhpSpecMatchers\Matchers;
use PhpSpec\Exception\Example\FailureException;
use PhpSpec\Matcher\BasicMatcher;
class BeFinalMatcher extends BasicMatcher
{
public function supports(string $name, $subject, array $arguments): bool
{
return $name === 'beFinal';
}
public function getPriority(): int
{
return 0;
}
protected function matches($subject, array $arguments): bool
{
$reflection = new \ReflectionClass($subject);
return $reflection->isFinal();
}
protected function getFailureException(string $name, $subject, array $arguments): FailureException
{
return new FailureException('Expected subject to not be final, but it is.');
}
protected function getNegativeFailureException(string $name, $subject, array $arguments): FailureException
{
return new FailureException('Expected subject to be final, but it is not.');
}
}
Whenever I try to call $this->beFinal(); with this configuration, the spec is broken and shows the following message: method [array:2] not found.. If I add an isFinal() method to the class I'm testing and return true for example, it passes for $this->shouldBeFinal(); and fails for $this->shouldNotBeFinal();, but I don't want to add that method. I should just work without it and for as far as I understand it should be able to work like that, right?
I've also tried adding custom suites to my phpspec.yml, like so:
suites:
matchers:
namespace: PhpSpecMatchers\Matchers
psr4_prefix: PhpSpecMatchers\Matchers
src_path: src/PhpSpecMatchers/Matchers
spec_prefix: spec/PhpSpecMathcers/Matchers
But that doesn't change anything. I've also tried to add the following config to phpspec.yml:
extensions:
PhpSpecMatchers\Extension:
php_spec_matchers:
src_path: src
spec_path: spec
That also doesn't change anything.
One other thing I've tried was to ditch the extension approach and just declare my mather in phpspec.yml, like so:
matchers:
- PhpSpecMatchers\Matchers\BeFinalMatcher
As you might expect: same results.
The loading in PhpSpecMatchers\Extension does get called (tested by a simple var_dump(…);), but it doesn't seem to reach anything within PhpSpecMatchers\Matchers\BeFinalMatcher, since I don't get any output from any var_dump(…);
I've followed tutorials and examples from symfonycasts, phpspec docs itself and some other github project I found, they're all almost identically to my code (except namespaces, directory structures and stuff like that), so I'm kind of at a loss here.
How do I get it to a point where I can successfully call $this->shouldBeFinal(); and $this->shouldNotBeFinal();?
Many thanks to whovere can help me out here.
P.S.: I've also posted this issue on phpspec's github.
So apparently the priority was too low (see this comment my phpspec's github issue). PhpSpec\Matcher\IdentityMatcher (where the shouldBe comes from) extends from PhpSpec\Matcher\BasicMatcher where the priority is set to 100.
Since mine was set to 0 it got to mine first (I think) and therefore didn't execute properly. I've set my priority to 101 and it works flawlessly (except I had switched the positive and negative messages, I found out).
Related
i want to select all users in the database that have the role ROLE_USER only but i get this problm when i call the function they say "Call to a member function getNbr() on null" i think bcoz i use Findby() , bcoz i use the same function in another call and it works great look at the code :
public function indexAction(Request $request)
{
$us = $this->getDoctrine()->getManager();
$locationus = $us->getRepository('AppBundle:Usr')->findBy(
[ 'roles' => ["ROLE_USER"] ]);
echo $nb_us = $locationus->getNbr();
if($authChecker->isGranted(['ROLE_ADMIN']))
{
return $this->render('settingAdmin/profiladmin.html.twig' , array(
'nb_us' => $nb_us,
));
}
and this is the other function in the UserRepository:
class UserRepository extends \Doctrine\ORM\EntityRepository
{
public function getNbr() {
return $this->createQueryBuilder('l')
->select('COUNT(l)')
->getQuery()
->getSingleScalarResult();
}
}
getNbr is method of UserRepository class, so it can be called only for this UserRepository class instance. This method returns total users count.
findBy returns array of entities (in you case all users with role ROLE_USER), not UserRepository class instance, so you can't use getNbr in context of this variable
If you want to get the length of array of entities (in you case all users with role ROLE_USER), just use count function:
echo $nb_us = count($locationus);
if($authChecker->isGranted(['ROLE_ADMIN']))
{
return $this->render('settingAdmin/profiladmin.html.twig' , array(
'nb_us' => $nb_us, 'locationus' => $locationus
));
}
There looks to be quite many things going on in the code there:
1) $us->getRepository('AppBundle:Usr') is probably typoed and should be $us->getRepository('AppBundle:User') instead (?) In general it would be safer to use $us->getRepository(AppBundle\User::class) so that syntax errors can be caught easier/earlier.
2) You are trying to invoke repository method on array with $locationus->getNbr() which is incorrect on multiple accounts (you cannot invoke functions on arrays - and repository methods cannot be invoked from entities either).
3) why is the code using echo?
4) as an additional note (assuming that this is roughly the full intended code), it would make sense to move all the getters & handling inside the if section so that the code will perform better (it doesn't do unnecessary database queries etc when the user doesn't have enough rights to access the view/information).
If I understood the intention correctly, in this case, the second repository function getNbr is superfluous here. If that is intending to just calculate the number of instances returned by the first find:
$locationus = $us->getRepository('AppBundle:User')->findBy(['roles' => ["ROLE_USER"] ]);
$nb_us = count($locationus);
Or alternatively (if you want to use and fix the getNbr repository function) then you don't need the first repository getter. This will require some rewriting of the repository function as well though:
$nb_us = $us->getRepository('AppBundle:User')->getNbr("ROLE_USER");
This is not about instructions (the docs are sufficient), but a question of how things work.
Symfony 4's autowiring system allows us to auto-inject services by simply typehinting
use App\Util\Rot13Transformer;
class TwitterClient
{
public function __construct(Rot13Transformer $transformer)
{
$this->transformer = $transformer;
}
}
To gain a deeper understanding of PHP, I have looked around in the symfony-bundle source code but can't find the spot where the "magic" happens.
How can Symfony prevent PHP from protesting that not enough arguments were fed to the constructor (or any function that uses autowiring)?
They use Refection
How can Symfony prevent PHP from protesting that not enough arguments were fed to the constructor
Reflection allows you to inspect the definition of other "things" in PHP. Among them are Classes their methods, and the arguments for those methods.
<?php
class bar
{
//just a placeholder class
};
$bar = new bar(); //instance of bar
//class to inspect
class foo
{
public function __construct( bar $bar)
{
//do something fancy with $bar
}
}
//get the Reflection of the constructor from foo
$Method = new ReflectionMethod('foo', '__construct');
//get the parameters ( I call them arguments)
$Args = $Method->getParameters();
//get the first argument
$Arg = reset($Args);
//export the argument definition
$export = ReflectionParameter::export(
array(
$Arg->getDeclaringClass()->name,
$Arg->getDeclaringFunction()->name
),
$Arg->name,
true
);
//parse it for the typehint
$type = preg_replace('/.*?(\w+)\s+\$'.$Arg->name.'.*/', '\\1', $export);
echo "\nType: $type\n\n";
var_dump(is_a($bar, $type));
Outputs:
Type: bar
bool(true)
You can see it here
Then you just use is_a() or whatever to see if an "input" object has bar as one of it's ancestors. And as you can see in this simplified example if we had object $bar, we would know that it's perfectly good as an input to our constructor because it returns true.
I should note SO is probably not the right place to ask this, but i could use it in one of my many projects so I didn't mind figuring it out. Also I never used Symphony...
Special thanks to this SO question for the last bit on parsing the type hint:
PHP Reflection - Get Method Parameter Type As String
That said I would have figured the Regx out in about 10 seconds, the export method no so much.
This is the extent of the documentation on it
http://php.net/manual/en/reflectionparameter.export.php
Literally
public static string ReflectionParameter::export ( string $function , string $parameter [, bool $return ] )
As others mentioned, they use Reflection. If you want to see how exactly Symfony is doing this, start with autowire() method here
I have 2 code's examples for 1
1)- In the first example- first fuction returns the result and pass it to the second fuction.
$goods_id = $_POST['goods_id'];
$goods_category = $this->getCategory($goods_id);
$goods_list = $this->getGoodsList($goods_category);
function getCategory($goods_id){
...
return $goods_category;
}
function getGoodsList($goods_category){
...
return $goods_list;
}
2)- In the second example- the first fuction uses the second function to get the value and then returns the result.
$goods_id = $_POST['goods_id'];
$goods_list = $this->getGoodsList($goods_id);
function getGoodsList($goods_id){
...
$goods_category = $this->getCategory($goods_id);
...
return $goods_list;
}
function getCategory($goods_id){
...
return $goods_category;
}
What code example i should use ($goods_category won't be used anywhere else)?
Before answering this you should complete the following prerequisites:
Post here the class(es) name(s), properties, methods signatures
Are ::getGoodsList() and ::getCategory($goods_id) methods of the same class or not?
Please use private/protected/private method visibility keywords even if you consider them both public
The two main questions: 1. whether ::getCategory($goods_id) method will ever be used somewhere outside the class visibility scope. 2. whether you need the item category below in the code once more. Answering 4.1 as "no" would yield 4.2 "no" as well.
If 4.1. is "yes" and 4.2. "yes", then declare ::getCategory($goods_id) as public and use your option #1. I.e. assign the category to $goods_category and use it anywhere below.
If 4.1. is "yes" and 4.2. "no", then declare ::getCategory($goods_id) as public and you can rewrite your code to something like
$goods_list = $class->getGoodsList($class->getCategory($goods_id));
Note that one should avoid >3-4 nested method calls. 2 is probably fine (my taste), but more than 5 in one line will make the line long and unreadable. However, it all depends on the app logic and memory usage and other stuff, so not a generic recommendation.
If 4.1. is "no" and 4.2. is "no", then declare it as protected or private if you don't plan to extend the class or you don't need the method in classes-descendants and use your approach #2:
class Util {
public function getGoodsList($goods_id) {
...
$goods_category = $this->getCategory($goods_id);
...
return $goods_list;
}
protected function getCategory($goods_id) {
...
return $goods_category;
}
}
I have a contract "ArticleStorage" that every storage must be subscribe to be valid for model.
True, this is not the problem, my problem is: pagination ... or "results modification", in this case at fetchAll, i want modify its behavior but without adding parameters, etc
<?php
interface ArticleStorage
{
// public function insert();
// public function update();
// public function delete();
public function fetchAll();
}
class MySQLArticleStorage implements ArticleStorage
{
public function fetchAll()
{
// SELECT * FROM `articles`;
}
}
?>
How my model works.
class ArticlesModel
{
public function __construct(ArticleStorage $storage)
{
}
}
in this case, I expect a "ArticleStorage" but do not know which "Storage" was given, true ... and i want to paginate or apply a results modification, using the Storage.
class MySQLArticleResultsModifier
{
public function __construct(MySQLArticleStorage $storage)
{
}
public function fetchAll()
{
// ...
}
}
In case of a pagination, how i can modify ArticleStorage fetchAll and apply my modified query ?
Is there a case where your model demands that a fetchall on top of another fetchall is possible; I don't think so, infact this is how you decide if you need a decorator or not, by answering this question to yourself
Is the decorator function you are thinking of making works like a decoration{like a real decoration where you can put stars on your christmas tree {decoration1}, and some toys on your tree {decoration2} at the same instance? Otherwise there is no point in making a decorator pattern, The nature of decorator is to decorate the concrete implementations from outside world, and change the output, without being affected by the other decoration being applied to a concrete instance.
Now as to the current implementation, I think #mrhobo is quite right, your fetch function might look like
public function fetch($limit, $order,$sort)
A very smart fetch could also expect the user to send a hashtable of key-value , of the columnname = value of column by using which you can make your own select query on the fly.
I use Redbeanphp ( http://redbeanphp.com/ ) in my php project. And i want to use a table prefix for my tables.
Redbeanphp can't support table prefix since the version 3.0. But i want to extend Redbeanphp to support table prefix in my project.
I don't want to modify the redbeanphp code. But if there's no solution, i'll do that.
I have already tried to replace the QueryWriter of Redbeanphp but the QueryWriter class is not always the same (it depends of the type of my database).
What is the best way to do that ?
I now got the response so i answer to myself.
Once redbean is initialized, you can configure a new toolbox. The toolbox in redbean handle 3 important objects : The query writer, the Redbean OODB and the database adapter. You can access the current redbean toolbox with R::$toolbox
You can do this code :
R::configureFacadeWithToolbox(new RedBean_ToolBox(R::$redbean, R::$adapter, R::$writer));
This code does nothing. Because you configure Redbean with a new toolbox but with the same OODB, the same database adapter and the same query writer. But in this code, you can replace one of these object by your own object.
Example, replacing the writer by a dummy writer :
$writer = new MyQueryWriter();
R::configureFacadeWithToolbox(new RedBean_ToolBox(R::$redbean, R::$adapter, $writer));
The probem is the following :
You want to replace the query writer by your own query writer to handle a table prefix
The query writer class is not always the same. Redbean use 5 classes for the query writer. The class depends of the database type. For instance, if you use a Mysql database, the query writer class is RedBean_QueryWriter_MySQL
You don't want to write an entire query writer.
Redbean query writer possible classes are :
RedBean_QueryWriter_CUBRID
RedBean_QueryWriter_MySQL
RedBean_QueryWriter_Oracle
RedBean_QueryWriter_PostgreSQL
RedBean_QueryWriter_SQLiteT
So, this is my solution. I wrote 5 littles classes.
class MyCubridQueryWriter extends RedBean_QueryWriter_CUBRID {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name);
return parent::safeTable($name, $noQuotes);
}
}
class MyMysqlQueryWriter extends RedBean_QueryWriter_MySQL {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
class MyOracleQueryWriter extends RedBean_QueryWriter_Oracle {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
class MyPostgreSqlQueryWriter extends RedBean_QueryWriter_PostgreSQL {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
class MySQLiteTQueryWriter extends RedBean_QueryWriter_SQLiteT {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
As you can see, each class extend a Redbean query writer class. We override the safeTable method. Redbean always use safeTable on a table name. The prefix function is simple :
function prefix($table) {
return "my_prefix_$table";
}
So now, in our code. We can use an array to map a Redbean query writer class to our own classes and replace it. Here we are :
$writerMapping = array(
'RedBean_QueryWriter_CUBRID' => 'MyCubridQueryWriter',
'RedBean_QueryWriter_MySQL' => 'MyMysqlQueryWriter',
'RedBean_QueryWriter_Oracle' => 'MyOracleQueryWriter',
'RedBean_QueryWriter_PostgreSQL' => 'MyPostgreSqlQueryWriter',
'RedBean_QueryWriter_SQLiteT' => 'MySQLiteTQueryWriter'
);
$class = $writerMapping[get_class(R::$writer)];
$writer = new $class(R::$adapter);
R::configureFacadeWithToolbox(new RedBean_ToolBox(R::$redbean, R::$adapter, $writer));
Et voila. Now Redbean will use your own writer and you can do what you want ! With our safeTable method, we add a prefix to every table name in the database.
I ran into this problem when wanting to use RedBean with Wordpress. My solution was to create another class (WPR for "wordpress redbean"), like so:
class WPR {
public static function __callStatic($method, $params)
{
global $wpdb;
$prefix = $wpdb->base_prefix;
foreach ($params as &$param)
$param = preg_replace('/\{([a-zA-Z0-9_]+)\}/', $prefix . '$1', $param);
// switch to wordpress database
R::selectDatabase('WPR');
// do the dang thing
$res = call_user_func_array(array('R',$method),$params);
// go back
R::selectDatabase('default');
// send it
return $res;
}
};
R::addDatabase('WPR', "mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASSWORD);
I also wanted this class to use a different database than my 'regular' redbean class, so I have the selectDatabase() calls in there. Comment them out if you don't need them.
What it does is it acts as a proxy to redbean, but with each input it checks for some substring like {this} and it expands it out into the full database name, with prefix. Here's an example of your usage:
$my_blog = WPR::find('{blogs}', 'domain=?', array('mydomain.com')); or
$allowed_hosts = WPR::getCol('SELECT domain FROM {blogs}');
In those two cases, {blogs} gets converted to wp_blogs
Magus,
I have the same problem as you. I tried your solution but could not get it working. I wrote a couple of functions for prefixing and my object names into table names and back, which I think will work in my case, but I'd still like to get your way working since it'll be more transparent. I have unprefixed table names working for reading and writing.
I noticed was that Oracle support isn't available out-of-the-box in RedBean, so I added checks for each classname to avoid errors:
if (class_exists('RedBean_QueryWriter_MySQL', false)) {
class MyMysqlQueryWriter extends RedBean_QueryWriter_MySQL {
...
}
The checks should work, I got output to my log within my MySQL (which I'm using) block while loading the prefixing code.
Also, at the end there you wrote:
$class = $writerMapping(get_class(R::$writer));
but you probably meant:
$class = $writerMapping[get_class(R::$writer)];
Based on some debugging, my R::$writer has been changed after configureFacadeWithToolbox, but, for some reason the table names aren't being converted, and nothing within my custom safeTable function is being executed.
If you could give any more info on how you tested your method or what I could be missing, I'd be glad to hear it.
(I'm sorry this message isn't an answer to your question, but I really couldn't find any other way to send you a message or comment on your answer. Damn Stack Overflow! (Just kidding, I love it.))