Гость
Форумы / PHP, Perl, Python [игнор отключен] [закрыт для гостей] / PHP - вопросы по ссылкам / 23 сообщений из 23, страница 1 из 1
13.10.2019, 21:39
    #39875720
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Не получается у меня решить несколько вопросов.
Не подскажите решение? Если это зависит от версии, то мне актуальна версия 5.4.

1. Допустим мне нужна функция, которая может принимать как byref, так и byval аргументы. То есть эту функцию я могу вызывать как func1($arr), так и func1([]) — в первом варианте в $arr после вызова функции могут быть внесены изменения.
Я не могу в ее декларации написать function func1(&$arg), потому что тогда второй синтаксис станет недоступен.
Если же использовать декларацию function func1($arg), то по всей видимости добраться до ссылки на $arg будет невозможно.
В интернете я нагуглил такой пример (способы с перехватом вывода и парсингом debug_zval_dump я вообще не рассматриваю):
Код: php
1.
2.
3.
4.
5.
6.
7.
function isReference($variable) {
    $variable = array($variable);
    $arg = func_get_arg(0);
    $isRef = isset($arg[0]) && $arg === array($variable[0]);
    $variable = $variable[0];
    return $isRef;
}


Однако я не пойму, как этот код работает (если он вообще работает), такая проверка:
Код: php
1.
2.
3.
4.
5.
$p = '123';
$r = &$p;
var_dump(isReference($p));
var_dump(isReference($r));
var_dump(isReference([]));


дает false для всех трех примеров.

2. Допустим у меня есть функция с произвольным числом аргументов. И в один из аргументов я хочу возвращать значения, если это потребуется. Внутри этой функции я использую func_get_args, то в этом случае до ссылок я добраться не могу.
Тут я придумал два способа, но первый способ начиная с 5.4 не работает (да и в любом случае такой способ мне не нравится), а второй способ имеет те же ограничения, что и первый вопрос (я не могу вызвать эту функцию и передать не ссылку):
Код: php
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
$p = [':a'=>111, ':b'=>22];

function func1()
{
        $args = debug_backtrace(false,0)[0]['args'];
        foreach($args as &$arg) $arg['test1'] = 'func1';
}

function func2(&$arg1=null,&$arg2=null,&$arg3=null,&$arg4=null)
{
        $arg1['test2'] = 'func2';
}

func1(?$p);
func2($p);
func2([]);
var_dump($p);
...
Рейтинг: 0 / 0
14.10.2019, 00:28
    #39875742
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Сочинил такой способ, максимально близкий к тому, что мне бы хотелось (за исключением дуального byval/byref аргумента функции, похоже это ограничение архитектуры):
Код: php
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
class DBEngine
{
        private $param = [];
        private $bind;
        function param($param=null, &$value=null)
        {
                $argn = func_num_args();
                if (!$argn) return $this->param;
                if (!isset($param)) return $this->param = [];
                if ($argn > 1) $this->param[$param] = &$value;
                if (!array_key_exists($param, $this->param)) return null;
                return $this->param[$param];
        }
        function query($sql, &$params=null, $options=null)
        {
                $this->param(null);
                if (isset($params)) foreach ($params as $key=>&$val) $this->param($key, $val);
                $this->proc();
                $this->exec();
                $tmp = $this->param(); foreach ($tmp as $key=>&$val) $params[$key] = &$val;
        }
        function proc()
        {
                $params = &$this->param;
                $this->bind(':a', $params[':a']);
                $this->bind(':c', $params[':c']);
        }
        function bind($label, &$var)
        {
                $this->bind[$label] = &$var;
        }
        function exec()
        {
                $ref = &$this->bind[':a']; $ref = 123;
                $ref = &$this->bind[':c']; $ref = 321;
        }
}

$db = new DBEngine();

$p = [':a'=>111, ':b'=>222];
$db->query(null, $p);
print_r($p);


Здесь методы bind и exec эмулируют методы внешней библиотеки oci8, а разбивка на методы query и proc обусловлена уже моими предпочтениями.
К внутреннему свойству params в методе proc я вынужден обращаться напрямую ($params = &$this->param;), потому что если получать его через функцию ($params = $this->param();), то я получаю не ссылку, а копию, и соответственно к методу bind я привязываю не исходные переменные, а копии. Из-за этого в реальном коде это свойство я вынужден делать protected, а не private.
Не посоветуете, что тут улучшить или переделать?
...
Рейтинг: 0 / 0
14.10.2019, 08:39
    #39875772
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Вот финальный вариант:
Код: php
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
function dbg($label, $var)
{
        print "$label  ##  ";
        ob_start();
        var_dump($var);
        $str = ob_get_contents();
        ob_end_clean();
        $str = preg_replace('/=>\n\s*/m', '=>', $str);
        print $str;
}

class ext
{
        private static $bind;
        static function bind($label, &$var)
        {
                self::$bind[$label] = &$var;
        }
        static function exec()
        {
                $ref = & self::$bind[':a']; $ref = 123;
                $ref = & self::$bind[':c']; $ref = 321;
        }
}

class DBEngine
{
        private $param = [];
        private function param($param=null, &$value=null)
        {
                $argn = func_num_args();
                if (!$argn) return $this->param;
                if (!isset($param)) return $this->param = [];
                if ($argn > 1) $this->param[$param] = &$value;
                if (!array_key_exists($param, $this->param)) return $tmp=null;
                return $this->param[$param];
        }
        public function query($sql, &$params=null, $options=null)
        {
                $this->param(null);
                if (isset($params)) foreach ($params as $key=>&$val) $this->param($key, $val);
                $this->proc();
                ext::exec();
                $tmp = $this->param(); foreach ($tmp as $key=>&$val) $params[$key] = &$val;
        }
        public function proc()
        {
                $params = &$this->param;
                ext::bind(':a', $params[':a']);
                ext::bind(':c', $params[':c']);
        }
}

// Проверка эмулятора для типового сценария
$v1 = 'abc';
$v2 = null;
dbg('before', ['v1'=>$v1, 'v2'=>$v2]);
ext::bind(':a', $v1);
ext::bind(':c', $v2);
ext::exec();
dbg('after', ['v1'=>$v1, 'v2'=>$v2]);

// Проверка эмулятора с моим классом
$db = new DBEngine();
$p = [':a'=>111, ':b'=>222];
dbg('before', $p);
$db->query(null, $p);
dbg('after', $p);



Он работает, но я бы хотел в методе proc() работать не через приватную переменную $param, а через приватный метод param().
Указать $params = &$this->param() я не могу — получаю сообщение о том, что получить ссылку можно только у переменной.
Если же указать $params = $this->param(), а в декларации метода задать возврат ссылки private function &param — то получаю Segmentation fault.
Что я делаю не так?
...
Рейтинг: 0 / 0
14.10.2019, 08:53
    #39875778
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Alibek B.задать возврат ссылки private function & param — то получаю Segmentation fault
...
Рейтинг: 0 / 0
14.10.2019, 16:08
    #39876082
TolikD
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Правильней будет так:
Код: php
1.
2.
3.
4.
5.
6.
private function &param($param=null, &$value=null) {
.....
return &$this->param[$param];
}
...
$params = &$this->param();
...
Рейтинг: 0 / 0
14.10.2019, 16:15
    #39876087
TolikD
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Немного ошибся
Код: php
1.
return $this->param[$param];
...
Рейтинг: 0 / 0
14.10.2019, 19:30
    #39876205
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Это как-бы невалидный код.
Я все эти варианты пробовал.

$params = &$this->param(); — неверный синтаксис, & может быть только у переменных, не у функций/методов.

return $this->param[$param]; — в моем варианте именно так и указано

private function &param — сразу же segfault
...
Рейтинг: 0 / 0
15.10.2019, 12:31
    #39876496
ScareCrow
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
а задлянафига оно тебе надо?
...
Рейтинг: 0 / 0
15.10.2019, 12:42
    #39876505
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Alibek B.мне нужна функция, которая может принимать как byref, так и byval аргументы. То есть эту функцию я могу вызывать как func1($arr), так и func1([]) — в первом варианте в $arr после вызова функции могут быть внесены изменения.
...
Рейтинг: 0 / 0
15.10.2019, 12:52
    #39876512
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Или речь про function &param() ?
Чтобы использовать protected-метод, а не protected-свойство.
...
Рейтинг: 0 / 0
15.10.2019, 13:14
    #39876537
ScareCrow
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
автормне нужна функция, которая может принимать как byref, так и byval аргументы
зачем?
...
Рейтинг: 0 / 0
15.10.2019, 13:26
    #39876551
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Это удобно в использовании.
...
Рейтинг: 0 / 0
15.10.2019, 15:03
    #39876611
Hett
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Наркомания какая-то.
...
Рейтинг: 0 / 0
15.10.2019, 15:08
    #39876616
ScareCrow
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
ArrayAccess https://www.php.net/manual/ru/class.arrayaccess.php

тебе в помощь.
...
Рейтинг: 0 / 0
15.10.2019, 16:39
    #39876661
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
ScareCrowArrayAccess тебе в помощь.
А в чем удобство?
Удобно — это когда я могу просто написать $db->query($sql, [':p1'=>111]) — в тех случаях когда мне не нужны возвращаемые параметры. А если нужны — тогда вначале создаю массив $p=[':p1'=>111], а затем указываю его при вызове метода.
Отсутствие возможности call-time pass-byref означает, что первый синтаксис мне недоступен и мне всегда нужно создавать массив, прежде чем вызвать метод. Это некоторое неудобство, хотя и терпимое.
А использование ArrayAccess означает, что мне все равно нужно будет создавать параметры отдельно от вызова метода, только их создание станет еще более многословным.
...
Рейтинг: 0 / 0
15.10.2019, 16:47
    #39876666
полудух
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Alibek B.Удобно — это когда я могу просто написать $db->query($sql, [':p1'=>111]) — в тех случаях когда мне не нужны возвращаемые параметры. А если нужны — тогда вначале создаю массив $p=[':p1'=>111], а затем указываю его при вызове метода.
это делается так:
Код: php
1.
2.
f1($var)    {f2($var);}
f2(&$var)   {do_something_w_var...;}


а вот ту всю ересь наверху - сжечь.
...
Рейтинг: 0 / 0
15.10.2019, 16:57
    #39876674
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
А как я получу измененные значения, вызвав f1?
Или мне нужно вызывать f1 или f2 в зависимости от того, требуется ли мне передать в них переменную или выражение?
Два варианта одного и того же метода с вариациями byval/byref — это еще более криво, чем использовать только byref и всегда создавать переменную для метода.
...
Рейтинг: 0 / 0
15.10.2019, 19:32
    #39876737
полудух
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
это вы так шутите да
кривее вот этого ничего уже не может быть во всей Галактике:
Alibek B.Сочинил такой способ, максимально близкий к тому, что мне бы хотелось (за исключением дуального byval/byref аргумента функции, похоже это ограничение архитектуры):
если у вас острая необходимость иметь возможность передать переменную без ссылки:
Код: php
1.
f1("будущая переменная")


то прямее этого ничего нет в принципе.

Alibek B.Или мне нужно вызывать f1 или f2 в зависимости от того, требуется ли мне передать в них переменную или выражение?
это ещё и не очевидно для вас
...
Рейтинг: 0 / 0
15.10.2019, 20:01
    #39876750
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Про такое говорят - ничего слаще моркови не ел.
Возможность передать аргумент по ссылке или по значению на выбор - это нормально, такое доступно во многих языках.
А считать невозможность этого нормальной, как и использование двух функций - это деформация.
Например мне нужно использовать магический метод __call и передать в него аргумент по ссылке, чтобы в него можно было вернуть значение. Но у магического метода нет декларируемых аргументов и способ передачи по ссылке ему объявить нельзя, только получить массив аргументов по значению. Ну или передавать в него объект с реализацией ArrayAccess, например.
Это иначе, как кривой подход, не назвать.
...
Рейтинг: 0 / 0
16.10.2019, 10:26
    #39876926
ScareCrow
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
если чтото надо получить из функции, это надо возвращать из функции через ключевое слово return.

или переходите на новую версию php там есть штатная возможность отличить передачу по ссылку от передачи по значению.
...
Рейтинг: 0 / 0
16.10.2019, 11:06
    #39876950
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
ScareCrowесли чтото надо получить из функции, это надо возвращать из функции через ключевое слово return.
Действительно:
https://www.php.net/manual/ru/function.oci-bind-by-name.php
https://www.php.net/manual/ru/mysqli-stmt.bind-param.php
https://www.php.net/manual/ru/function.sort.php

ScareCrowили переходите на новую версию php там есть штатная возможность отличить передачу по ссылку от передачи по значению.
Возможно потом так и сделаю, но пока нужно использовать 5.4.
...
Рейтинг: 0 / 0
17.10.2019, 17:03
    #39877812
TolikD
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Alibek B.Я все эти варианты пробовал.


Я не вижу, где ты пробовал. В твоих примерах нет метода с корректным возвратом по ссылке. А я указал корректный метод. Ты его пробовал?
Код: php
1.
function &foo(...)
...
Рейтинг: 0 / 0
22.09.2020, 11:10
    #40001221
Alibek B
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
PHP - вопросы по ссылкам
Снова подниму тему, но уже по синтаксису.
У меня есть класс с таким методом:
Код: php
1.
final public function query($sql, &$params=null, $options=null)


Метод я могу вызывать например так:
Код: php
1.
->query('select 1')


А вот так не могу:
Код: php
1.
2.
3.
->query('select 1', , 'hash')
->query('select 1', null, 'hash')
->query('select 1', [], 'hash')


Приходится использовать так:
Код: php
1.
->query('select 1', $emptyvar, 'hash')


А хотелось бы без использования переменных.

Можно ли это как-то решить на уровне деклараций?
Например в некоторых ЯП перезагрузка функций определяется количеством аргументов и их типов.
...
Рейтинг: 0 / 0
Форумы / PHP, Perl, Python [игнор отключен] [закрыт для гостей] / PHP - вопросы по ссылкам / 23 сообщений из 23, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]