How to set mysqli_result::num_rows - php

For mocking purposes, I extended the mysqli_result class:
class MysqliResultMock extends mysqli_result {
...
}
Now, I need to override what it returns for its member called 'num_rows', but unfortunately with $this->num_rows = 123; it's simply not settable (I get a fatal error for writing this line:
Fatal error: Cannot directly set the property MysqliResultMock::$num_rows in DbTest.php on line 13)
Can it be done somehow?
Thank you.

Haha, I figured out the answer.
You just need to add the public $num_rows; line to the class, explicitly, to make it writable. Like this:
class result extends mysqli_result {
public $num_rows;
public function __construct()
{
$this->num_rows = 6;
...
then you can call mysql_num_rows() on an instance and it will return the given num_rows properly.

Related

Php - making a class instance that has dependencies in the constructor

I have a class, that has a constructor that looks like this:
use App\Libraries\Content\ContentInterface;
use EllipseSynergie\ApiResponse\Laravel\Response;
class ImportController extends Controller
{
private $indexable;
function __construct(Response $response, ContentInterface $contentInterface) {
$this->indexable = \Config::get('middleton.wp.content.indexable_types');
$this->response = $response;
$this->contentInterface = $contentInterface;
}
public function all() {
$include = array_diff($this->indexable, ['folder']);
$importResult = $this->import($include);
$this->deleteOldContent($importResult['publishedPostsIDs']);
return $importResult['imported'];
}
How can I instantiate this class from another class and call the method all() from it?
I have tried with something like this:
use EllipseSynergie\ApiResponse\Laravel\Response;
use App\Libraries\Content\ContentInterface;
class ContentImport extend Command {
public function handle() {
(new ImportController(new Response, new ContentInterface))->all();
}
But, that doesn't work, I get the error that I should pass the arguments to the Response class too:
[Symfony\Component\Debug\Exception\FatalThrowableError]
Type error: Too few arguments to function EllipseSynergie\ApiResponse\AbstractResponse::__construct(), 0 passed in /home/
vagrant/Projects/middleton/app/Console/Commands/ContentImport.php on line 43 and exactly 1 expected
What is the correct way of doing this?
I believe this should work
use EllipseSynergie\ApiResponse\Laravel\Response;
use App\Libraries\Content\ContentInterface;
class ContentImport extend Command {
public function handle(ImportController $importController) {
$importController->all();
}

PHPUnit cannot mock __get function on mock object

I am trying to mock a mysqli object for my unit testing and therefore I have to either mock the property mysqli_result::__get() or mock the property mysqli_result::num_rows directly.
I already looked for a solution but the answers I found were just not working.
My Code looks like this:
use PHPUnit\Framework\TestCase;
class Mocks extends TestCase{
public function makeMysqliMock(array $queries): mysqli{
// build mocked mysqli-object
#$link = $this
# ->getMockBuilder("mysqli")
# // set methods
# ->setMethods(['__get', "query", "real_escape_string"])
# ->getMock();
$link = $this->createMock("mysqli");
// set method 'real_escape_string'
$link->expects($this->any())
->method("real_escape_string")
->will($this->returnCallback(function($str){
return addslashes($str);
}));
// set method 'query'
$link->expects($this->any())
->method("query")
->will($this->returnCallback(function(string $query) use ($queries){
// pick the query result for the current test
$data = isset($queries[$query]) ? $queries[$query] : null;
// build mocked 'mysqli_result'
if (is_array($data)){
$result = $this
->getMockBuilder("mysqli_result")
->setMethods(["fetch_assoc", "__get"])
->disableOriginalConstructor()
->getMock();
// build simulated fetch method
$result->expects($this->any())
->method("fetch_assoc")
->withAnyParameters()
->will($this->returnCallback(function() use ($data){
// set internal result pointer
static $index = 0;
// return current result - increment pointer
return isset($data[$index]) ? $data[$index++] : false;
}));
$result->expects($this->at(0))
->method("__get")
->with($this->equalTo("mysqli_num_rows"))
->will($this->returnValue(count($data)));
return $result;
}else {
return is_null($data) ? false : 1;
}
}));
return $link;
}
}
When I run that code phpunit gives me the following error message:
C:\xampp\htdocs\werimanage\php>php phpunit-6.0.11.phar tests
PHPUnit 6.0.11 by Sebastian Bergmann and contributors.
PHP Fatal error: Method Mock_mysqli_result_d3aa5482::__get() must take exactly 1 argument in phar://C:/xampp/htdocs/werimanage/php/phpunit-6.0.11.phar/phpunit-mock-objects/Generator.php(263) : eval()'d code on line 53
Fatal error: Method Mock_mysqli_result_d3aa5482::__get() must take exactly 1 argument in phar://C:/xampp/htdocs/werimanage/php/phpunit-6.0.11.phar/phpunit-mock-objects/Generator.php(263) : eval()'d code on line 53
So I do not know what the error is or how to fix it. I would really appreciate your help. Thanks!
according to the php documentation the method __get requires 1 argument (more over this is the error phpUnit is returning).
You can see on the documentation :
public mixed __get ( string $name )
The $name argument is the name of
the property being interacted with.
I imagine that you will be calling the __get method foreach of your column in your sql request. You then have to mock all the calls and the returns of the __get method with each argument (each column).
Hope this will help you.
This error happens because magic method __get must accept one parameter -- the name of the property. When PHPUnit generates the code to declare the mock it uses the declaration of the method from the original class. Since there is no method __get declared for mysqli_result PHPUnit mock generator declares it as __get() without parameters, and this is encountered as error.
If you really want to do your coding and testing the way you do it, you can use the following approach:
class SuperMock extends mysqli_result {
public function __get($name){
}
}
class Mocks extends PHPUnit_Framework_TestCase
{
public function testGet()
{
$data = ['one' => 1, 'two' => 2];
$mock = $this->getMockBuilder('SuperMock')
->disableOriginalConstructor()
->setMethods(['__get', 'fetch_assoc'])
->getMock();
$mock->expects($this->once())
->method('__get')
->with('nonexistent')
->willReturn(42);
$mock->expects($this->once())
->method('fetch_assoc')
->willReturn($data);
$this->assertInstanceOf('mysqli_result', $mock);
$this->assertSame(42, $mock->nonexistent);
$this->assertSame($data, $mock->fetch_assoc());
}
public function testDirectProperty(){
$mock = $this->getMockBuilder('SuperMock')
->disableOriginalConstructor()
->setMethods(['__get', 'fetch_assoc'])
->getMock();
$mock->nonexistent = 42;
$this->assertSame(42, $mock->nonexistent);
}
}
This will technically solve the issue. Still I would suggest revising your testing strategy. Because now it looks like you are testing how the interaction with a database is performed. But do you really need to test if data is fetched as an associative array, and that number of rows is figured out through mysqli_num_rows property? Going this way makes tests too much coupled to the production code. I believe this is the case when it is more suffucient to test the result being retrieved rather than the internal details of this process.

PHP: Type hinting in namespaces not working?

I'm trying to do some type hinting with PHP 5.6 while using namespaces and I keep getting errors like this:
PHP Catchable fatal error: Argument 1 passed to
NS\MyClass::__construct() must be an instance of string, string
given, called in
/Users/username/path/MyClass.php
on line 9 and defined in
/Users/username/path/MySubClass.php
on line 52
The code basically looks like this:
<?php namespace NS;
class MySubClass extends MyClass {
public function __construct(string $mystring) {
parent::__construct($mystring);
}
}
?>
and
<?php namespace NS;
class MyClass {
public function __construct() {
// do stuff
}
}
?>
so when I call the constructor of the Subclass with a String like
$x = new \NS\MySubClass("hello");
the error shows up.
It also does NOT work with a
public function __construct(\string $mystring) {
How can I typehint a basic type while in a namespace?
It is not possible to typehint strings in PHP 5.6. Support for this feature was added in PHP 7.
More info on typehinting for functions in PHP.

PDO not returning an object, but an array

With the following code, PDO won't return my $parcel as an object, but as an array. There for when i try to call my objects function, it will fail. Every example i have found has done this, in a similar way to mine. What am i doing wrong?
$statement = $this->connection->query($query);
$statement->setFetchMode(PDO::FETCH_CLASS, 'Parcel');
while ($parcel = $statement->fetch()) {
echo $parcel->hello();
}
The Parcel class, if interested.
class Parcel {
public $id;
public $parcel_number;
public $registred_at;
public $shipped_by;
public $shipped_at;
function __construct($parcel_number)
{
$this->parcel_number = $parcel_number;
}
public function hello(){
return "World";
}
}
And im using folder structures to structure the code and an Autoloader, that can probaly affect the PDO's way of calling the object.
While this is the error code
Call to a member function info() on a non-object
This error means the object is null or not instantiated properly, you might need to specify full path to your class.
For example
$statement->setFetchMode(PDO::FETCH_CLASS, 'app\model\Parcel');

Magento Fatal Error

Its my first try with Magento.
I get this Error Message:
Fatal error: Call to a member function append() on a non-object in /var/customers/webs/magento/magento/app/code/core/Mage/Install/controllers/WizardController.php on line 77
Maybe someone have an Idee or can help me with my Problem?
Now the whole Code from Line 68 - 79:
protected function _prepareLayout()
{
$this->loadLayout('install_wizard');
$step = $this->_getWizard()->getStepByRequest($this->getRequest());
if ($step) {
$step->setActive(true);
}
$leftBlock = $this->getLayout()->createBlock('install/state', 'install.state');
$this->getLayout()->getBlock('left')->append($leftBlock);
return $this;
}
Its the orginal code i havenĀ“t edit anything
$this->getLayout()->getBlock('left')
should return a block class or NULL.
If it returned a block class then :
abstract class Mage_Core_Block_Abstract extends Varien_Object
has function
append($leftBlock);
This means that your $this->getLayout()->getBlock('left')
is not return a block instance.
Why not do a mage log of what $this->getLayout()->getBlock('left')
returns and confirm.

Categories