Рекурсивно слить массив в расширенный класс

У меня есть следующий код в php:

<?php
interface someInterface {
public function foo();
}

class A implements someInterface {
public function foo(){
return [
'a' => 'some value',
'b' => 'some value',
'c' => [
'ca' => 'some value',
'cb' => 'some value',
'cc' => 'some value'
]
];
}
}

class B extends A {
public function foo(){
return [
'a' => 'some value 2',
'c' => [
'ca' => 'some value 3',
'cb' => 'some value 4'
]
];
}
}

Делать следующее

$b = new B();
var_dump($b->foo());

должен дать мне следующий вывод

[
'a' => 'some value 2',
'b' => 'some value',
'c' => [
'ca' => 'some value 3',
'cb' => 'some value 4',
'cc' => 'some value'
]
]

Кто-нибудь знает, как этого добиться?

Любой может продлить class A и я не хочу, чтобы другие пользователи использовали вручную array_merge_recursive для достижения этого.

заранее спасибо

0

Решение

Вы можете иметь класс в середине, который делает работу. У него должен быть другой метод, который будет унаследован и позже использован. Единственное условие здесь — не использовать foo() публично, но getFoo() вместо

interface someInterface {
public function foo();
}class A implements someInterface {
public function foo(){
return [
'a' => 'some value',
'b' => 'some value',
'c' => [
'ca' => 'some value',
'cb' => 'some value',
'cc' => 'some value'
]
];
}
}

class Middle extends A {
public function getFoo() {
return array_merge_recursive(parent::foo(), $this->foo());
}
}

class B extends Middle {
public function foo(){
return [
'a' => 'some value 2',
'c' => [
'ca' => 'some value 3',
'cb' => 'some value 4'
]
];
}
}

$b = new B();
var_dump($b->getFoo());

Это также может быть достигнуто путем foo() защищенный и магия __call() метод, который делает работу getFoo(), Потом звоню $b->foo() сработает __call() Таким образом, в результате чего parent а также this контексты foo() и выплевывая их.

Кстати, рекурсивное слияние массивов не приведет к точно такому же выводу, который вы хотите, в любом случае, вы можете делать любую логику, которая вам нужна getFoo() (или в __call())

Проблема здесь может быть в том, что если foo() имеет более низкий модификатор доступа, чем public Вы не можете иметь это в интерфейсе. К счастью, абстрактные классы могут иметь абстрактные защищенные методы, которые обязывают дочерний объект реализовать этот метод.

Пример сценария __call() способ и обязывая членов иметь protected foo() может быть:

abstract class Base {
abstract protected function foo();
}

abstract class A {
protected function foo(){
return [
'a' => 'some value',
'b' => 'some value',
'c' => [
'ca' => 'some value',
'cb' => 'some value',
'cc' => 'some value'
]
];
}
}

class Middle extends A {
public function __call($name, $args) {
if ($name == 'foo')
return array_merge_recursive(parent::foo(), $this->foo());
}
}

class B extends Middle {
protected function foo(){
return [
'a' => 'some value 2',
'c' => [
'ca' => 'some value 3',
'cb' => 'some value 4'
]
];
}
}

$b = new B();
var_dump($b->foo());

Сюда B будут некоторые проблемы с автозаполнением foo.
Если один не знает «взломать» при написании $b-> не получит предложение для foo(),
Немного больше хаков может решить эту проблему как минимум Netbeans а также PHPStorm

Аннотируя это Middle имеет общественность foo() метод до определения Middle учебный класс:

/**
* @method foo
*/
class Middle extends A {

Позже, комментируя, что случай B на самом деле является примером Middle:

$b = new B();
/* @var $b Middle */

Потом писать $b-> приведет к предложению foo() метод.

Может быть лучше, если есть другой класс, который наследует B и не имеет методов, но только аннотации, но, как я понял, классы, как B будут написаны от других людей, и они могут быть не знакомы с этим соглашением.

1

Другие решения

Вы можете попробовать это:

<?php

interface someInterface
{
public function foo();
}

class A implements someInterface
{
public function foo()
{
return [
'a' => 'some value',
'b' => 'some value',
'c' => [
'ca' => 'some value',
'cb' => 'some value',
'cc' => 'some value'
]
];
}
}

class B extends A
{
public function foo()
{
$parent = parent::foo();
$arr = [

'a' => 'some value 2',
'c' => [
'ca' => 'some value 3',
'cb' => 'some value 4'
]
];return $this->mergeArrays($arr, $parent);

}

private function mergeArrays($ar1, $ar2)
{
foreach ($ar1 as $key => $item) {
if (is_array($item)) {
if (isset($ar2[$key])) {
$ar2[$key] = $this->mergeArrays($item, $ar2[$key]);
} else {
$ar2[$key] = $item;
}
} else {
$ar2[$key] = $item;
}
}
return $ar2;
}

}$b = new B();
var_dump($b->foo());

Выход:

array (3) {[«a»] => string (12) «some value 2» [«b»] => string (10) «some
value «[» c «] => array (3) {[» ca «] => string (12)» some value 3 «[» cb «] =>
string (12) «некоторое значение 4» [«cc»] => string (10) «некоторое значение»}}

0