Yielding from a PHP method - oop

I am trying to use yield in PHP 7.3, from a class method:
<?php
class X {
public $x;
public function __construct($x) {
$this->x = $x;
}
public function call_direct() {
return $this->x;
}
public function call_yield() {
return function() {
yield $this->x;
};
}
}
$a = new X(42);
echo "direct: " . $a->call_direct(), "\n";
$iter = $a->call_yield();
foreach ($iter as $n) {
echo "yielded: $n\n";
}
Naturally, this outputs direct: 42... but not the second line. What am I missing to get it to output yielded: 42 as well? Is this actually possible in PHP these days?
(Obviously my actual example iterates over a list, but I have trimmed it down to the essential).

Related

Doctrine ORM create method phpspec test failure

I try to write, at first glance, it would seem a trivial test for my repository's "update" method:
<?php
declare(strict_types=1);
namespace Paneric\Authorization\ORM\Repository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\ORMException;
use Paneric\Authorization\DTO\FieldDTO;
use Paneric\Authorization\ORM\Entity\Field;
use Doctrine\ORM\EntityRepository;
use Paneric\Authorization\Interfaces\FieldRepositoryInterface;
class FieldRepository extends EntityRepository implements FieldRepositoryInterface
{
const ENTITY_CLASS = Field::class;
public function __construct(EntityManagerInterface $_em)
{
parent::__construct($_em, $_em->getClassMetadata(self::ENTITY_CLASS));
}
...
public function update(int $fieldId, FieldDTO $fieldDTO): void
{
try {
$field = $this->find($fieldId);
$field->transfer($fieldDTO);
$this->_em->flush();
} catch (ORMException $e) {
echo $e->getMessage();
}
}
...
}
with a spec method:
<?php
namespace spec\Paneric\Authorization\ORM\Repository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\QueryBuilder;
use Paneric\Authorization\DTO\FieldDTO;
use Paneric\Authorization\ORM\Entity\Field;
use Paneric\Authorization\ORM\Repository\FieldRepository;
use Doctrine\ORM\AbstractQuery;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class FieldRepositorySpec extends ObjectBehavior
{
public function let(EntityManagerInterface $_em, ClassMetadata $classMetadata)
{
$_em->getClassMetadata(Field::class)->willReturn($classMetadata);
$this->beConstructedWith($_em);
}
...
public function it_updates(Field $field, FieldDTO $fieldDTO, EntityManagerInterface $_em)
{
$fieldId = 1;
$field = $this->find($fieldId);
$field->transfer($fieldDTO)->shouldBeCalled();
$_em->flush()->shouldBeCalled();
$this->update($fieldId, $fieldDTO);
}
...
}
and receive the following error:
Unexpected method call on Double\EntityManagerInterface\EntityManagerInterface\P1:
- find(
null,
1,
null,
null
)
expected calls were:
- getClassMetadata(
exact("Paneric\Authorization\ORM\Entity\Field")
)
- find(
exact("Paneric\Authorization\ORM\Entity\Field"),
exact(1)
)
- flush(
)
Apparently issue is related to the call:
...
$field = $this->find($fieldId);
...
Although the second remark related to getClassMetadata, looks strange, considering the fact that my spec let method:
public function let(EntityManagerInterface $_em, ClassMetadata $classMetadata)
{
$_em->getClassMetadata(Field::class)->willReturn($classMetadata);
$this->beConstructedWith($_em);
}
does its job in case of other spec tests.
Can anyone help me to solve this issue ? Thx in advance.
In my repository's "update" metod, line:
$field = $this->find($fieldId);
has to be replaced by:
$field = $this->_em->find(Field::class, $fieldId);
so the complete spec test looks like:
public function it_updates(Field $field, FieldDTO $fieldDTO, EntityManagerInterface $_em)
{
$fieldId = 1;
$_em->find(Field::class, $fieldId)->willReturn($field);
$field->transfer($fieldDTO)->shouldBeCalled();
$_em->flush()->shouldBeCalled();
$this->update($fieldId, $fieldDTO);
}

Parent-driven determination that can end in class change

I'm trying to make a use from Steam API data as I like to learn on live examples, and looking at the way various statistics are returned I began to think that OOP approach would suit me best in this case.
What I'm trying to achieve is to loop through all the results, and programatically populate an array with objects of type that corresponds to the actual type of the statistic. I've tried to build myself a basic class, called Statistic, and after instantiating an object determine wheter or not it's class should change (i.e. whether or not to cast an object of type that Statistic is parent to and if so, of what type). How to do that in PHP? My solution gives me no luck, all of the objects are of type Statistic with it's 'type' property being the object I want to store alone in the array. Code:
$data = file_get_contents($url);
$data = json_decode($data);
$data = $data->playerstats;
$data = $data->stats;
$array;
for($i=0;$i<165;$i++)
{
$array[$i] = new Statistic($data[$i]);
echo "<br/>";
}
var_dump($array[10]);
And the classes' code:
<?php
class Statistic
{
public function getProperties()
{
$array["name"] = $this->name;
$array["value"] = $this->value;
$array["type"] = $this->type;
$array["className"] = __CLASS__;
return json_encode($array);
}
public function setType($x)
{
$y = explode("_",$x->name);
if($y[0]=="total")
{
if(!isset($y[2]))
{
$this->type = "General";
}
else
{
if($y[1]=="wins")
{
$this->type = new Map($x);
$this->__deconstruct();
}
if($y[1]=="kills")
{
$this->type = new Weapon($x);
$this->__deconstruct();
}
else $this->type="Other";
}
}
else $this->type = "Other";
}
function __construct($obj)
{
$this->name = $obj->name;
$this->value = $obj->value;
$this->setType($obj);
}
function __deconstruct()
{
echo "deconstructing <br/>";
return $this->type;
}
}
class Weapon extends Statistic
{
public function setType($x)
{
$y = explode("_",$x);
if($y[1]=="kills")
{
$this->type = "kills";
}
else if($y[1]=="shots")
{
$this->type = "shots";
}
else if($y[1]=="hits")
{
$this->type = "hits";
}
}
function __construct($x)
{
$name = explode("_",$x->name);
$this->name = $name[2];
$this->value = $x->value;
$this->setType($x->name);
}
function __deconstruct()
{
}
}
class Map extends Statistic
{
public function setType($x)
{
if($x[1]=="wins")
{
$this->type = "wins";
}
if($x[1]=="rounds")
{
$this->type = "rounds";
}
}
public function setName($name)
{
if(isset($name[3]))
{
if(isset($name[4]))
{
return $name[3] + " " + $name[4];
}
else return $name[3];
}
else return $name[2];
}
function __construct($x)
{
$name = explode("_",$x->name);
$this->name = $this->setName($name);
$this->value = $x->value;
$this->setType($name);
}
function __deconstruct()
{
}
}
Gives the result:
object(Statistic)#223 (3) {
["name"]=> string(18) "total_kills_deagle"
["value"]=> int(33)
["type"]=> object(Weapon)#222 (3) {
["name"]=> string(6) "deagle"
["value"]=> int(33)
["type"]=> string(5) "kills" }
}
Should that determination be driven from the loop itself, the whole advantage of having a set of functions that does everything for me and returns a ready-to-serve data is gone, since I would really have to cast different objects that aren't connected to each other, which is not the case here. How can I achieve returning objects of different type than the object itself is?
For answer your question How can I achieve returning objects of different type than the object itself is?
"Casting to change the object's type is not possible in PHP (without using a nasty extension)"
For more info: Cast the current object ($this) to a descendent class
So you can't change the class type of an instance with type of a derived class. In other world can't change instance of Static with instance of Weapon.

PHP 7: Function which creates different classes with "new class"

If I create a class with PHP7 multiple times, it seems it is returning always the same class instead of returning a new one each time.
For example:
function createAClass()
{
return new class
{
public static $foo=0;
};
}
$class = createAClass();
$class::$foo = 3;
echo "class:".$class::$foo."<br>";
$anotherClass = createAClass();
echo "anotherClass:".$anotherClass::$foo."<br>";
This is the output:
class:3
anotherClass:3
I though the output should be 3 and 0. What's happening? Is this a bug, or a "feature" of PHP 7? :)
btw, I was trying to use this to test a trait with static methods with PHPUnit.
I've found the documentation and it seems it is the expected behaviour:
http://php.net/manual/en/language.oop5.anonymous.php
All objects created by the same anonymous class declaration are instances of that very class.
But I needed to create a function which returned a different class each time it was called, so I came with this solution:
function createAClass()
{
$class = null;
$stamp = random_int(PHP_INT_MIN ,PHP_INT_MAX);
echo "stamp: $stamp\n";
$classcode = <<< EOT
\$class = new class {
public static \$differentiate='$stamp';
// YOUR CODE HERE
};
EOT;
echo "Source code of class:\n$classcode\n\n";
eval($classcode);
return $class;
}
$class = createAClass();
$anotherClass = createAClass();
if (get_class($class) === get_class($anotherClass)) {
echo 'same class';
} else {
echo 'different class';
}

General internet "scraping" question

I just started studying programming about 6 months ago and I have really been diving deep into Objective-C. Unfortunately, I don't know any programmers IRL to bounce general questions off of.
What languages are being used when people write programs that will search a website for information and then send it back? For example, if I wanted to write a program that would search weather.com for the daily temperature of the last 30 days in a given location and then send it back as say...an NSArray or NSDictionary, how would i do that? Can I do that in Objective C or is that super-advanced scripting language stuff? If I CAN do it in Objective-C, can someone link to a tutorial or place that may get me started learning that type of stuff? (I don't really know the term for this type of programming so my google searches have been unfruitful.)
I most commonly use PHP and MySQL with CURL
http://en.wikipedia.org/wiki/CURL
You can do some fun things like Search Engine Results Page queries, etc.
Here is the source from a crawler I use. I've cut out some parts for anonymity's sake, but it's a good almost-working example. I can help you get it running if need be.
<?php
class Crawler {
protected $markup = '';
protected $uri = '';
protected $db_location = "localhost";
protected $db_username = "***";
protected $db_password = "***";
protected $db_name = "***";
public function __construct() {
ini_set('memory_limit', -1);
}
public function getMarkup() {
$markup = "";
$markup = #file_get_contents($this->uri);
return $markup;
}
public function get($type) {
$method = "_get_{$type}";
if (method_exists($this, $method)){
return call_user_method($method, $this);
}
}
protected function db_query($query) {
$connection = mysql_connect($this->db_location,$this->db_username,$this->db_password) or die(mysql_error());
mysql_select_db($this->db_name,$connection) or die(mysql_error()." >> ".$query);
//echo $query."<br/>"; //for debugging
$result = mysql_query($query,$connection) or die (mysql_error()." >> ".$query);
$i = 0;
if($result != 1)
{
while ($data_array = mysql_fetch_array($result))
{
foreach($data_array as $key => $value)
{
$tableArray[$i][$key] = stripslashes($data_array[$key]);
}
$i++;
}
return $tableArray;
}
}
protected function db_insert($table,$array) {
$tableArray = $this->db_query("show columns from ".$table);
$inputString = "";
foreach($tableArray as $key => $value)
{
if (array_key_exists($value[0], $array) && $value[0]) {
$inputString .= "'".addslashes($array[$value[0]])."', ";
} else {
$inputString .= "'', ";
}
}
$inputString = substr($inputString, 0, -2);
$this->db_query("insert into $table values(".$inputString.")");
return mysql_insert_id();
}
protected function _get_data() {
//$scrape['id'] = $this->get('id');
$scrape['name'] = $this->get('name');
$scrape['tags'] = $this->get('tags');
$scrape['stat_keys'] = $this->get('stat_keys');
$scrape['stat_values'] = $this->get('stat_values');
foreach($scrape['stat_values'] as $key => $value) {
$scrape['stat_values'][$key] = trim($scrape['stat_values'][$key]);
if(strpos($value,"<h5>Featured Product</h5>")) {
unset($scrape['stat_values'][$key]);
}
if(strpos($value,"<h5>Featured Company</h5>")) {
unset($scrape['stat_values'][$key]);
}
if(strpos($value,"<h5>Featured Type</h5>")) {
unset($scrape['stat_values'][$key]);
}
if(strpos($value,"sign in")) {
unset($scrape['stat_values'][$key]);
}
if(strpos($value,"/100")) {
unset($scrape['stat_values'][$key]);
}
}
if(sizeof($scrape['tags']) > 0 && is_array($scrape['tags'])) {
foreach($scrape['tags'] as $tag) {
$tag_array[$tag] = $tag_array[$tag] + 1;
}
$scrape['tags'] = $tag_array;
foreach($scrape['tags'] as $key => $tag_count) {
$scrape['tags'][$key] = $tag_count - 1;
}
}
$scrape['stat_values'] = array_merge(array(),$scrape['stat_values']);
return $scrape;
}
protected function _get_images() {
if (!empty($this->markup)){
preg_match_all('/<img([^>]+)\/>/i', $this->markup, $images);
return !empty($images[1]) ? $images[1] : FALSE;
}
}
protected function _get_links() {
if (!empty($this->markup)){
preg_match_all('/<a([^>]+)\>(.*?)\<\/a\>/i', $this->markup, $links);
return !empty($links[1]) ? $links[1] : FALSE;
}
}
protected function _get_id() {
if (!empty($this->markup)){
preg_match_all('/\/wine\/view\/([^`]*?)-/', $this->markup, $links);
return !empty($links[1]) ? $links[1] : FALSE;
}
}
protected function _get_grape() {
if (!empty($this->markup)){
preg_match_all('/ class="linked" style="font-size: 14px;">([^`]*?)<\/a>/', $this->markup, $links);
return !empty($links[1]) ? $links[1] : FALSE;
}
}
}
if($_GET['pass'] == "go") {
$crawl = new Crawler();
$crawl->go();
}
?>
So, you want to know how to write server-side code? Well, in theory you can write that in whatever you want. I also assure you it isn't "super-advanced".
You might find it easiest to get started with PHP. W3schools.com has a fine tutorial.
What you are describing is a crawler (e.g. Google).
Any language that has the ability to send HTTP requests and receive responses can do this (which is most languages).
If you don't care to code this thing from scratch, try downloading an open source crawler framework that will allow for custom plugins to parse the resulting HTML.
For your example, you would tell the crawler what site you want it to crawl (i.e. your weather site), add URI constraints if necessary, and create a custom plugin to parse the weather data out of the HTML it responds with. You can then save that data however you see fit.

PHP static objects giving a fatal error

I have the following PHP code;
<?php
component_customer_init();
component_customer_go();
function component_customer_init()
{
$customer = Customer::getInstance();
$customer->set(1);
}
function component_customer_go()
{
$customer = Customer::getInstance();
$customer->get();
}
class Customer
{
public $id;
static $class = false;
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
}
else
{
return self::$class;
}
}
public function set($id)
{
$this->id = $id;
}
public function get()
{
print $this->id;
}
}
?>
I get the following error;
Fatal error: Call to a member function set() on a non-object in /.../classes/customer.php on line 9
Can anyone tell me why I get this error? I know this code might look strange, but it's based on a component system that I'm writing for a CMS. The aim is to be able to replace HTML tags in the template e.g.;
<!-- component:customer-login -->
with;
<?php component_customer_login(); ?>
I also need to call pre-render methods of the "Customer" class to validate forms before output is made etc.
If anyone can think of a better way, please let me know but in the first instance, I'd like to know why I get the "Fatal error" mentioned above.
Well, I think your Customer::getInstance() method is flawed. It should look like this:
...
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
return self::$class; // ADDED!!
}
else
{
return self::$class;
}
}
....
In the if(self::$class == false) branch you are creating the instance of the class, but you dont return it.
You could also rewrite it as such:
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
}
return self::$class;
}
To make it a bit shorter.
DRY: Don't Repeat Yourself
static function getInstance()
{
if(self::$class == false)
{
self::$class = new Customer;
}
return self::$class;
}
And for Sinlgetons it is also important to prevent __clone() from being used. Making it private should solve that problem:
private function __clone() {}