How to make arrays in which the key is number and string.
<?php
$array = array
(
'test' => 'thing',
'blah' => 'things'
);
echo $array[0]; // thing
echo $array[1]; // things
echo $array['test']; // thing
echo $array['blah']; // things
?>
$array = array_values($array);
but why would you need that? can you extend your example?
You could use array_keys to generate a lookup array:
<?php
$array = array
(
'test' => 'thing',
'blah' => 'things'
);
$lookup = array_keys ($array);
// $lookup holds (0=>'test',1=>'blah)
echo $array[$lookup[0]]; // thing
echo $array[$lookup[1]]; // things
echo $array['test']; // thing
echo $array['blah']; // things
?>
You could implement your own class that "implements ArrayAccess"
For such class you can manually handle such behaviour
UPD: implemented just for fun
class MyArray implements ArrayAccess
{
private $data;
private $keys;
public function __construct(array $data)
{
$this->data = $data;
$this->keys = array_keys($data);
}
public function offsetGet($key)
{
if (is_int($key))
{
return $this->data[$this->keys[$key]];
}
return $this->data[$key];
}
public function offsetSet($key, $value)
{
throw new Exception('Not implemented');
}
public function offsetExists($key)
{
throw new Exception('Not implemented');
}
public function offsetUnset($key)
{
throw new Exception('Not implemented');
}
}
$array = new MyArray(array(
'test' => 'thing',
'blah' => 'things'
));
var_dump($array[0]);
var_dump($array[1]);
var_dump($array['test']);
var_dump($array['blah']);
Related
What would be the best way to perform some modification on a method argument before the method is actually executed?
I have tried achieving this with a combination of attributes and use of the decorator/proxy pattern:
#[\Attribute]
class Query
{
}
class Foo
{
#[Query]
public function request(array $query = [])
{
}
public function foo(array $query = [])
{
}
#[Query]
public function bar(string $name, int $age, array $query = [])
{
}
}
class FooDecorator
{
private Foo $target;
public function __construct(Foo $target)
{
$this->target = $target;
}
public function __call(string $methodName, array $args)
{
$class = get_class($this->target);
try {
$reflection = new \ReflectionClass($class);
$methods = $reflection->getMethods();
$attributeName = __NAMESPACE__ . '\Query';
foreach ($methods as $method) {
if ($method->getName() !== $methodName) {
continue;
}
$attributes = $method->getAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getName() === $attributeName) {
$parameters = $method->getParameters();
foreach ($parameters as $key => $param) {
if ($param->getName() === 'query') {
// Here we filter the parameter named 'query'
$args[$key] = $this->validateQueryParameter($args[$key]);
break;
}
}
}
}
}
} catch (\Exception $e) {
}
if (method_exists($this->target, $methodName)) {
return call_user_func_array([$this->target, $methodName], $args);
}
}
private function validateQueryParameter(array $query): array
{
$allowed = [
'foo',
'bar',
];
$query = array_filter($query = array_change_key_case($query), function ($key) use ($allowed) {
// Filter out any non-allowed keys
return in_array($key, $allowed);
}, ARRAY_FILTER_USE_KEY);
return $query;
}
}
$foo = new FooDecorator(new Foo());
// Should remove faz & baz, but keep foo in the query
$foo->query(['faz' => 1, 'baz' => 2, 'foo' => 3]);
// Should not perform anything on the query parameter
$foo->foo(['baz' => 1]);
// Should remove faz & baz, but keep foo in the query
$foo->bar('foo', 100, ['faz' => 1, 'baz' => 2, 'foo' => 3]);
This works as expected, but since I am using a decorator here I am now missing the hints for each method included in the Foo class.
I know that I could use an interface and declare all methods included in Foo, but then I would need to implement every method (the real class contains many many methods) in the decorator, which seems like overkill.
I am trying to create a class that is going to generate dynamic class properties according to a user input.
There will be an array created from user input data. This array should work as an example:
$array = array(
# The boolean values are not relevant in this example
# The keys are important
'apple' => true,
'orange' => false,
'pear' => false,
'banana' => true,
);
Right now I want to create a new class with the array keys as the class properties:
class Fruit {
public $apple;
public $orange;
public $pear;
public $banana;
(etc.)
}
I had to manually write down all four properties now.
Is there a way to make it automated?
<?php
class MyClass
{
public function __construct ($config = [])
{
foreach ($config as $key => $value) {
$this->{$key} = $value;
}
}
}
$myClass = new MyClass(['apple' => 1, 'orange' => 2]);
echo $myClass->apple;
?>
this should help you
Here you go,
I put a few bonus things in there:
class MyClass implements Countable, IteratorAggregate
{
protected $data = [];
public function __construct (array $data = [])
{
foreach ($data as $key => $value) {
$this->{$key} = $value;
}
}
public function __set($key, $value){
$this->data[$key] = $value;
}
public function __get($key)
{
if(!isset($this->{$key})) return null; //you could also throw an exception here.
return $this->data[$key];
}
public function __isset($key){
return isset($this->data[$key]);
}
public function __unset($key){
unset($this->data[$key]);
}
public function __call($method, $args){
$mode = substr($method, 0, 3);
$property = strtolower(substr($method, 3)); //only lowercase properties
if(isset($this->{$property})) {
if($mode == 'set'){
$this->{$property} = $args[0];
return null;
}else if($mode == 'get'){
return $this->{$property};
}
}else{
return null; //or throw an exception/remove this return
}
throw new Exception('Call to undefined method '.__CLASS__.'::'.$method);
}
//implement Countable
public function count(){
return count($this->data);
}
//implementIteratorAggregate
public function getIterator() {
return new ArrayIterator($this->data);
}
}
Test it:
$myClass = new MyClass(['one' => 1, 'two' => 2]);
echo $myClass->two."\n";
//Countable
echo count($myClass)."\n";
//dynamic set
$myClass->three = 3;
echo count($myClass)."\n";
//dynamic get/set methods. I like camel case methods, and lowercase properties. If you don't like that then you can change it.
$myClass->setThree(4);
echo $myClass->getThree()."\n";
//IteratorAggregate
foreach($myClass as $key=>$value){
echo $key.' => '.$value."\n";
}
Outputs
2 //value of 2
2 //count of $data
3 //count of $data after adding item
4 //value of 3 after changing it with setThree
//foreach output
one => 1
two => 2
three => 4
Test it online
Disclamer
Generally though it's better to define the class by hand, that way things like IDE's work. You may also have issues because you won't necessarily know what is defined in the class ahead of time. You don't have a concrete definition of the class as it were.
Pretty much any method(at least in my code) that starts with __ is a PHP magic method (yes, that's a thing). When I first learned how to use these I thought it was pretty cool, but now I almost never use them...
Now if you want to create an actual .php file with that code in it, that's a different conversation. (it wasn't 100% clear, if you wanted functionality or an actual file)
Cheers.
I would like to use the list() statement in combination with an object.
$tuple = new Tuple();
// ..
list ($guestbook, $entry, $author) = $tuple;
This would work if $tuple was an array with three elements. But its an object.
Is there any way without using a method of Tuple (returning that kind of array) like implementing a fancy native interface I yet don't know?
You can implement the interface ArrayAccess to do so:
class Tuple implements ArrayAccess {
private $arr;
public function __construct($arr) {
$this->arr = $arr;
}
public function offsetExists($offset) {
return array_key_exists($offset, $this->arr);
}
public function offsetGet($offset) {
return $this->arr[$offset];
}
public function offsetSet($offset, $value) {
return $this->arr[$offset] = $value;
}
public function offsetUnset($offset) {
unset($this->arr[$offset]);
}
}
$tuple = new Tuple([1, 2, 3]);
list($am, $stram, $gram) = $tuple;
echo $am;
echo $stram;
echo $gram;
// outputs: 123
See this previous post:
Convert PHP object to associative array
I am assuming (I haven't tested it) you could then do:
$tuple = new Tuple();
list ($guestbook, $entry, $author) = (array) $tuple;
You can do this:
$tumple = new Tumple();
$properties = get_object_vars($tumple);// ["guestbook" => "Feel", "entry" => "Good", "author" => "Inc"];
I have a class, that actually operates over a complicated array to make the manipulation more simple. The format of original array looks like this:
array(
array(
"name" =>"foo",
"type" =>8, //The array is NBT format and 8 stands for string
"value" =>"somevalue"
)
}
The class takes array like above as constructor:
class NBT_traverser implements ArrayAccess {
function __construct(&$array) {
$this->array = $array;
}
}
Then, this is how the members are accessed:
$parser = new NBT_traverser($somearray);
echo $parser["foo"]; //"somevalue"
When I print_R the class, I get the list of its values and the original complicated array. Like this:
object(NBT_traverser)#2 (1) {
["nbt":"NBT_traverser":private]=> &array(1) {
/*Tons of ugly array contents*/
}
Instead, I'd like to get this like the output of print_r:
array(
"foo" => "somevalue"
)
Is it possible to trick the print_r to do this? Current behavior makes it harder to debug with the class, than without it.
Of course, I can write my own method to print it, but I want to make the usage more simple for the users of the class. Instead, I wanted to give print_R something, that It will print as array.
You should not have issues if you are extending ArrayAccess just write a method to get your values
Example
$random = range("A", "F");
$array = array_combine($random, $random);
$parser = new NBT_traverser($array);
echo $parser->getPrint();
Output
Array
(
[A] => A
[B] => B
[C] => C
[D] => D
[E] => E
[F] => F
)
Class Used
class NBT_traverser implements ArrayAccess {
private $used; // you don't want this
protected $ugly = array(); // you don't want this
public $error = 0202; // you don't want this
private $array = array(); // you want this
function __construct(&$array) {
$this->array = $array;
}
function getPrint() {
return print_r($this->array, true);
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->array[] = $value;
} else {
$this->array[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->array[$offset]);
}
public function offsetUnset($offset) {
unset($this->array[$offset]);
}
public function offsetGet($offset) {
return isset($this->array[$offset]) ? $this->array[$offset] : null;
}
}
You could use the __toString function in your class
class Test
{
private $_array = array();
public function __toString()
{
return print_r($this->_array, true);
}
}
And then just echo out your class
$test = new Test();
echo $test;
I think this would print out your array as you want it to be?
Array
(
)
if i have two arrays i.e
$text = 'i am passed :)';
$fn = array(
':)',
':-)',
';;)'
)
$rep = array(
'smily1',
'smily2',
'smily3'
);
$output = str_replace($fn, $rep, $text);
echo $output;
i want to make a class for this to use in future where i will want...
how can i make a class for it...
and also how can i create a function for this...
Basically by wrapping your function in a class. If you're looking for more advanced functionality then that, you'll have to specify.
<?php
class SmileyFilter {
private $_keys;
private $_values;
function add($key, $value) {
$this->_keys[] = $key;
$this->_values[] = $value;
}
function add_all($pairs) {
foreach ($pairs as $key => $value)
$this->add($key, $value);
}
function replace($text) {
return str_replace($this->_keys, $this->_values, $text);
}
}
// usage
$s = new SmileyFilter();
$s->add(':)', 'smily1');
$s->add(':-)', 'smily2');
$s->add(';;)', 'smily3');
/* OR
$smileys = array(
':)' => 'smily1',
':-)' => 'smily2',
';;)' => 'smily3');
$s->add_all($smileys);
*/
$s->replace('i am passed :)'); // "i am passed smily1"
?>
class SmileyReplacer
{
protected static $_map = array(
':)' => 'smiley1',
':-)' => 'smiley2',
';;)' => 'smiley3'
);
public static function replace($string)
{
return str_replace(array_keys(self::$_map), self::$_map, $string);
}
}
// Usage
echo SmileyReplacer::replace('I am happy :)'); // I am happy smiley1
I see no reason why this should be instantiated, so an all static class is fine. There is no real state in it. You could add a static method addMap(array $map) which you could pass an associate array in case you want to feed the map from outside.
If you are concerned about the calls to array_keys each time you run replace, do benchmark. I highly doubt you can come up with enough smileys so it would really have an impact on performance.
Smiley
class Smiley {
private $name;
private $data;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getData() {
return $this->data;
}
public function setData($data) {
$this->data = $data;
return $this;
}
function __construct($name = null, $data = null) {
$this->name = $name;
$this->data = $data;
}
}
SmileyMapper
class SmileyMapper {
private $smilies = array();
public function addSmiley(Smiley $smiley) {
$this->smilies[] = $smiley;
return $this;
}
public function replaceSmileys($str) {
return str_replace(
array_map(
create_function(
'Smiley $item',
'return $item->getData();'
),
$this->smilies),
array_map(
create_function(
'Smiley $item',
'return $item->getName();'
),
$this->smilies),
$str
);
}
}
Example
$text = 'i am passed :)';
$fn = array(
':)',
':-)',
';;)'
);
$rep = array(
'smily1',
'smily2',
'smily3'
);
$sm = new SmileyMapper();
foreach ($fn as $k => $v) {
$sm->addSmiley(new Smiley($rep[$k],$v));
}
echo $sm->replaceSmileys($text);