Php из какого файла вызвана функция
Проверяет, есть ли в списке определённых функций, как встроенных (внутренних), так и пользовательских, функция function .
Список параметров
Имя функции в виде строки.
Возвращаемые значения
Возвращает true , если function существует и является функцией, иначе возвращается false .
Замечание:
Эта функция возвращает false для языковых конструкций, таких как include_once или echo .
Примеры
if ( function_exists ( 'imap_open' )) echo "Функции IMAP доступны.
\n" ;
> else echo "Функции IMAP недоступны.
\n" ;
>
?>?php
Примечания
Замечание:
Обратите внимание, что название функции может присутствовать, даже если саму функцию невозможно использовать из-за настроек конфигурации или опций компиляции (например, как для функций image).
Смотрите также
- method_exists() - Проверяет, существует ли метод в данном классе
- is_callable() - Проверяет, что значение может быть вызвано как функция в текущей области видимости
- get_defined_functions() - Возвращает массив всех определённых функций
- class_exists() - Проверяет, был ли объявлен класс
- extension_loaded() - Определяет, загружен ли модуль
User Contributed Notes 23 notes
For instance Wordpress uses it to make functions "pluggable." If a plugin has already defined a pluggable function, then the WP code knows not to try to redefine it.
But function_exists() will always return true unless you wrap any later function definition in a conditional clause, like if()<. >. This is a subtle matter in PHP parsing. Some examples:
if ( function_exists ( 'foo' )) <
print "foo defined\\n" ;
> else <
print "foo not defined\\n" ;
>
function foo () <>
if ( function_exists ( 'bar' )) <
print "bar defined\\n" ;
> else <
print "defining bar\\n" ;
function bar () <>
>
print "calling bar\\n" ;
bar (); // ok to call function conditionally defined earlier
print "calling baz\\n" ;
baz (); // ok to call function unconditionally defined later
function baz () <>
qux (); // NOT ok to call function conditionally defined later
if (! function_exists ( 'qux' )) <
function qux () <>
>
?>
Prints:
foo defined
defining bar
calling bar
calling baz
PHP Fatal error: Call to undefined function qux()
Any oddities are probably due to the order in which you include/require files.
It should be noted that the function_exists check is not relative to the root namespace. This means that the namespace should be appended to the check:
if (! function_exists ( __NAMESPACE__ . '\example' ))
PHP supports nested function based on certain criteria.
Please look over the code.
function Audio()
echo "Plugged Audo 5.1:
";
function Volume()
echo "Volume controls:
";
function Equalizer()
echo "Equalize Bands:
";
>
>
>
//Call to nested functions
Audio();
Volume();
Equalizer();
if(function_exists('Volume')):
echo "TRUE";
else:
echo "FALSE";
endif;
Case 1: //Result :Works Well
--------
Audio();
Volume();
Equalizer();
Case 2: //Results Notice Error. Root function Audio must be called first.
--------
Volume();
Case 3: //Results Error. Root function Volume must be called.
--------
Audio();
Equalizer();
Note :
The nested function should be called based on their order used.
In our example when Audio is not called and instantly when we try to call Volume puts under error.
Even though there is an possibility to use nested functions in PHP. It looks overhead to do so. Better to avoid in logical ground of script.
Tested on PHP 5.5.32
This is not going to go down as you might expect it should (even if you play smart and require/include_once):
if( function_exists ( 'my_function' ))
throw new Exception ( "'my_function' is already defined!" );
>
function my_function ()
// Do the work here
>
?>
This, however does work:
if( ! function_exists ( 'my_function' ))
function my_function ()
// Do the work here
>
>
else
throw new Exception ( "'my_function' is already defined!" );
>
?>
Does it have anything to do with PHP parse/execute phases or global/local scope or those curly brackets or something else, I have no idea, but the latter ugly son works, while the former bombs out claiming that 'my_function' is already defined.
Thought this might save someone a few minutes of debugging time.
function_exists will return false for functions disabled with the disable_functions ini directive. However those functions are still declared so trying to define them yourself will fail.
if(! function_exists ( 'readfile' )) <
function readfile ( $file ) <
$handle =@ fopen ( $cache , "r" );
echo @ fread ( $handle , filesize ( $file ));
@ fclose ( $handle );
>
>
?>
The above will issue a "Cannot redeclare readfile()" fatal error if readfile was disabled with disable_functions.
Functions within a function are better off as anonymous returns from create_function(), unless you want to be able to call it elsewhere.
However, I have used this in skinning: I use alert_box() to display certain errors, like a faulty SQL query. This simply calls display_alert(), which is defined in my skin scripts. However, alert_box() is sometimes called before I know which skin to load, so it has its own functionality which it uses if function_exists('display_alert') returns false.
To prevent direct calls to included files i use the following technique.
In the main file create an empty function with a random name. Like so:
function hjudejdjiwe () < return true ; >
?>
Then check for the existence of this function within your include:
if (! function_exists ( 'hjudejdjiwe' )) < die( '!' ); >
?>
Simple but effective.
If you use suhosin.executor.func.blacklist instead of disabled_functions in your php.ini, function_exists will return true for a disabled function. I used this to have the same beahviour with suhosin.executor.func.blacklist and disabled_functions:
function suhosin_function_exists ( $func ) if ( extension_loaded ( 'suhosin' )) $suhosin = @ ini_get ( "suhosin.executor.func.blacklist" );
if (empty( $suhosin ) == false ) $suhosin = explode ( ',' , $suhosin );
$suhosin = array_map ( 'trim' , $suhosin );
$suhosin = array_map ( 'strtolower' , $suhosin );
return ( function_exists ( $func ) == true && array_search ( $func , $suhosin ) === false );
>
>
return function_exists ( $func );
>
?>
I would like to comment on the following post:
A note of caution: function_exists() appears to be case-insensitive (at least as of PHP 4.3.8). e.g.:
function MyCasedFunction () return true ;
>
// Will return true, even though casing is "wrong"
if ( function_exists ( "mYcAsEdFuNcTiOn" ))
echo "I see it!" ;
?>
I believe that function calls itself are case insensitve, so this function is returning a valid truth. PHP doesn't care about cases.
The confusion expressed in some of the submissions here arise because functions declared outside conditional blocks are defined as the code is loaded and are thus callable and exist wherever in the code they are declared, whereas those declared inside a condition block are not defined until that block is executed. Thus:
echo foo();
function foo()
yields : "I am foo"
however because those inside a conditional are defined as they are encountered during code execution
yields: Fatal error: Uncaught Error: Call to undefined function foo()
i was wondering whether is_callable or function exists is faster when checking class methods.
my results when doing each operation 10000 times with a simple test class were the following:
is_callable: 0.28671383857727 seconds
function_exists: 0.14569997787476 seconds
(following tests have proved this to be true).
thus you can see, function_exists is twice as fast as is_callable.
This can be used to conditionally define a user function. In this sense, it can act as a sort of inline include_once().
For example, suppose you have a function A that calls function B. B is only used inside function A and is never called from anywhere else in the script. It's logical (and perfectly legal in PHP) to define B inside of A's definition, like so:
function A ( $inputArray )
<
if (! function_exists ( 'B' ))
<
function B ( $item )
<
// Do something with $item
// and return result
return $result ;
>
>
foreach ( $inputArray as $nextItem ) $outputArray [] = B ( $nextItem );
return $outputArray ;
>
?>
Without the function_exists test, you would get a fatal error the second time you called A, as PHP would think you were trying to redefine B (not legal in PHP). The placement of the test is also important. Since the if block is executed sequentially, like any other block of code, it must come before any call to the function defined within.
I, too, was wondering whether is_callable or function exists is faster when checking class methods. So, I setup the following test:
function doTimes ( $start , $end )
$start_time = explode ( " " , $start );
$start_time = $start_time [ 1 ] + $start_time [ 0 ];
$end_time = explode ( " " , $end );
$end_time = $end_time [ 1 ] + $end_time [ 0 ];
$time = $end_time - $start_time ;
return $time ;
>
class test
function test ()
return true ;
>
>
$callableIsTrue = false ;
$startIsCallable = microtime ();
for( $i = 0 ; $i < 10000 ; $i ++)
if( is_callable (array( 'test' , 'test' ))) < $callableIsTrue = true ; >
>
$endIsCallable = microtime ();
$existsIsTrue = false ;
$startExists = microtime ();
for( $i = 0 ; $i < 10000 ; $i ++)
if( function_exists ( 'test::test' )) < $existsIsTrue = true ; >
>
$endExists = microtime ();
$timeIsCallable = doTimes ( $startIsCallable , $endIsCallable );
$timeExists = doTimes ( $startExists , $endExists );
echo "is_callable keyword">.( $callableIsTrue ? "TRUE" : "FALSE" ). ", \n" ;
echo "function_exists keyword">.( $existsIsTrue ? "TRUE" : "FALSE" ). "
\n" ;
echo "
Did 10000 is_callables in " . $timeIsCallable . " seconds" ;
echo "
Did 10000 function_exists in " . $timeExists . " seconds" ;
?>
This gives the output :
is_callable = TRUE, function_exists = FALSE
Did 10000 is_callables in 0.0640790462494 seconds
Did 10000 function_exists in 0.0304429531097 seconds
So the fact that function_exists is twice as fast is slightly over shadowed by the fact that it doesn't work on class methods, at least not as far as I can tell.
I stumbled over the same problem as "eddiec" (users not able or not willing to use "_once"-suffixes).
A possible alternative explanation for the behavior:
If a file is included, it is possibly parsed every include-time.(?)
While parsing, every function in global scope is tried to register. THIS gets wrong, when multiple times included, and it produces an error.
If functions are defined within block scopes, their registration seems to be delayed until execution of such a block. Thus, not the function "function_exists" functions wrong, but simply the philosophy of the interpreter produces such results.
Thus, the same effect can be achieved by simply putting block braces around the contents of an include_once file:
if (function_exists('function_in_question')) return;
function function_in_question(. )
.
>
. other stuff
>
. which is equivalent to.
if (!function_exists('function_in_question'))
function function_in_question(. )
.
>
. other stuff
>
/*PHP doesn't Support nested functions. I have tried following in PHP_VERSION - 5.1.2*/
?phpfunction B () function C () function D ()<>
>
>
IsFunctionExist ( 'A' );
IsFunctionExist ( 'B' );
IsFunctionExist ( 'C' );
IsFunctionExist ( 'D' );
function IsFunctionExist ( $funcName ) echo function_exists ( $funcName )? " $funcName exist
" : " $funcName doesn't exist
" ;
>
?>
/*O U T P U T
A exist
B exist
C doesn't exist
D doesn't exist
*/
// If you want to chack if the function is enabled or disable in php.ini you can use this function:
function func_enabled ( $func ) $disabled = explode ( ',' , ini_get ( 'disable_functions' ));
foreach ( $disabled as $disableFunction ) $is_disabled [] = trim ( $disableFunction );
>
if ( in_array ( $func , $is_disabled )) $it_is_disabled [ "m" ] = $func . '() has been disabled for security reasons in php.ini' ;
$it_is_disabled [ "s" ] = 0 ;
> else $it_is_disabled [ "m" ] = $func . '() is allow to use' ;
$it_is_disabled [ "s" ] = 1 ;
>
return $it_is_disabled ;
>
?>
// An example of how to use:
$if_is_disabled = func_enabled ( 'exec' ); // return Arrey
echo $if_is_disabled [ "m" ]; // return text value
echo '
' ;
echo $if_is_disabled [ "s" ]; // return 1 or 0
?>
function_exists returns false on NULL and empty string:
if ( function_exists ( '' )) echo "empty string function exists\n" ;
>
if ( function_exists ( NULL )) echo "NULL function exists\n" ;
>
?>
Neither of the echo statements happen when I run this.
function_exists() does not cache its query internally.
by executing the following code
By executing the
There is a 0.16878 seconds improvement, when you use static array to cache methods existence.
to bob at thethirdshift dot net
regarding is_callable vs function_exists.
using your code
is_callable = TRUE, function_exists = FALSE
Did 10000 is_callables in 0.0443360805511 seconds
Did 10000 function_exists in 0.0111110210419 seconds
then we replace
is_callable(array('test','test'));
with
$callarray = array('test','test'); // place this outside for-loop
is_callable($callarray);
is_callable = TRUE, function_exists = FALSE
Did 10000 is_callables in 0.0314660072327 seconds
Did 10000 function_exists in 0.0120670795441 seconds
then we replace
is_callable(array('test','test'));
with
is_callable('test','test');
is_callable = TRUE, function_exists = FALSE
Did 10000 is_callables in 0.00991606712341 seconds
Did 10000 function_exists in 0.0113790035248 seconds
I hope you can see that loop-testing functions is not so simple. :)
Note that function_exists will return TRUE in the following situation, presumably because the function "testfunc" was defined when the script was PARSED/ITERPRETED, before the function_exists call was made at RUNTIME:
if ( function_exists ( 'testfunc' )) return;
function testfunc () < >
?>
So, this construction is not useful for preventing testfunc from being multiply defined if the script is muliply included or required.
However, the following construction DOES work to prevent multiple defines of testfunc:
if (! function_exists ( 'testfunc' )) <
function testfunc () < >
>
?>
CONTRAST this with similar uses of defined() which is completely runtime evaluated. These both work:
if ( defined ( 'testfunc_defined' )) return;
define ( 'testfunc_defined' , 1 );
function testfunc () < >
?>
AND.
if (! defined ( 'testfunc_defined' )) <
define ( 'testfunc_defined' , 1 );
function testfunc () < >
>
to avoid direct calls this can be better than function_exists
in the parent file:
define ( "IN_MODULE" , true );
?>
and in the target file:
if(! defined ( "IN_MODULE" )) die( "Can't access the file directly" );
?>
to php at fluidthoughts dot com
you aren't comparing against false, that if compares against true, so it's going to return that, as false != true
true being 1, True, (bool)True, etc.
false being 0, False, Null, etc
$foo = null ;
if ( $foo ) < echo 'yay' ; >//does not echo yay, because null is not True
$foo = false ;
if ( $foo ) < echo 'yay' ; >//does not echo yay, because false is not True
$foo = null ;
if (! $foo ) < echo 'yay' ; >//echoes yay!
$foo = false ;
if (! $foo ) < echo 'yay' ; >//echoes yay!
?>
This..
if (! function_exists ( '' )) echo "empty string function doesnt exist as compared as negative\n" ;
>
if (! function_exists ( NULL )) echo "NULL function doesnt exist as compared as negative\n" ;
>
?>
Works.
Alternatively, to enter upon existance..
Use your code. Since they dont exist, it wont enter..
to brooklynphil at hotmail dot com:
Your post is misleading, namely the 3rd and last speedtest you describe is an unfair comparison:
is_callable ( 'test' , 'test' );
?>
The value of the 2nd parameter $syntax_only is 'test' and this evaluates to boolean true. In other words, this is exactly the same as calling the function like this:
is_callable ( 'test' , true );
?>
Of course this will be faster because is_callable only does a very basic syntaxcheck. From the documentation: 'It will only reject simple variables that are not strings, or an array that does not have a valid structure to be used as a callback.'
If you omit this erroneous 3rd test, then according to your examples function_exists is 2 to 4 times faster.
Этот пост посвящён оптимизации PHP с помощью профайлера Blackfire в PHP-скрипте. Нижеприведённый текст является подробным техническим объяснением статьи в блоге Blackfire.
Обычно применяется метод strlen:
Однако такой вариант примерно на 20% медленнее этого:
Выглядит неплохо. Наверняка вы уже собрались открыть ваши исходники и заменить все вызовы strlen() на isset(). Но если внимательно прочитать оригинальную статью, то можно заметить, что причина 20-процентной разницы в производительности — многократные вызовы strlen(), порядка 60-80 тысяч итераций.
Дело не в том, как strlen() вычисляет длины строк в PHP, ведь все они уже известны к моменту вызова этого метода. Большинство по возможности вычисляется еще во время компиляции. Длина PHP-строки, отправляемой в память, инкапсулируется в С-структуру, содержащую эту самую строку. Поэтому strlen() просто считывает эту информацию и возвращает как есть. Вероятно, это самая быстрая из PHP-функций, потому что она вообще ничего не вычисляет. Вот ее исходный код:
Учитывая, что isset() не является функцией, причина 20-процентного проигрыша в производительности strlen() по большей части заключается в сопутствующих задержках при вызове функции в движке Zend.
Есть и ещё один момент: при сравнении производительности strlen() с чем-либо ещё добавляется дополнительный opcode. А в случае с isset() используется лишь один уникальный opcode.
Пример дизассемблированной структуры if(strlen()):
А вот семантически эквивалентная структура if(isset()):
Как видите, в коде isset() не задействуется вызов какой-либо функции (DO_FCALL). Также здесь нет opcode IS_SMALLER (просто проигнорируйте операторы RETURN); isset() напрямую возвращает булево значение; strlen() же сначала возвращает временную переменную, затем она передаётся в opcode IS_SMALLER, а уже финальный результат вычисляется с помощью if(). То есть в структуре strlen() используется два opcode, а в структуре isset() — один. Поэтому isset() демонстрирует более высокую производительность, ведь одна операция выполняется обычно быстрее, чем две.
Давайте теперь разберёмся, как в PHP работают вызовы функций и чем они отличаются от isset().
Сложнее всего анализировать ту часть виртуальной машины (момента исполнения кода PHP), которая связана с вызовами функций. Я постараюсь изложить самую суть, не углубляясь в моменты, имеющие отношение к вызовам функций.
Для начала разберём время выполнения (runtime) вызовов. Во время компиляции (compile time) на выполнение операций, связанных с PHP-функциями, требуется много ресурсов. Но если вы будете использовать кэш opcode, то во время компиляции у вас не будет проблем.
Предположим, что у нас скомпилировался некий скрипт. Давайте проанализируем только то, что происходит во время выполнения. Вот как выглядит дамп opcode вызова внутренней функции (в данном случае strlen()):
Для понимания механизма вызова функции, необходимо знать две вещи:
- вызов функции и вызов метода — это одно и то же
- вызов пользовательской функции и вызов внутренней функции обрабатываются по-разному
Дело в том, что независимо от того, известна PHP эта функция или нет, он не генерирует такой же opcode во время компиляции. Очевидно, что внутренние PHP-функции известны во время компиляции, поскольку они объявляются до того, как запускается компилятор. Но ясности в отношении пользовательских функций может и не быть, потому что они могут вызываться ещё до своего объявления. Если говорить об исполнении, то внутренние PHP-функции эффективнее пользовательских, к тому же им доступно больше механизмов валидации.
Из приведённого выше примера видно, что для управления вызовами функций используется больше одного opcode. Также нужно помнить, что функции имеют собственный стек. В PHP, как и в любом другом языке, для вызова функции сначала нужно создать стековый кадр и передать в него аргументы функции. Затем вы вызываете функцию, которая эти аргументы подтянет из стека для своих нужд. По завершении вызова вам придётся уничтожить созданный ранее кадр.
Так в общем виде выглядит схема работы с вызовами функций. Однако в PHP предусмотрена оптимизация процедур создания и удаления стекового кадра; кроме того, можно отложить их выполнение, чтобы не приходилось делать все эти телодвижения при каждом вызове функции.
Opcode SEND_VAR отвечает за отправку аргументов в стековый кадр. Компилятор в обязательном порядке генерирует такой opcode до вызова функции. Причём для каждой переменной создаётся свой собственный:
Здесь вы видите ещё один opcode — SEND_VAL. Всего существует 4 вида opcode для отправки чего-либо в стек функции:
- SEND_VAL: отправляет значение константы (строковое, целочисленное и т.д.)
- SEND_VAR: отправляет PHP-переменную ($a)
- SEND_REF: отправляет PHP-переменную в виде ссылки в функцию, которая принимает аргумент ссылкой
- SEND_VAR_NO_REF: оптимизированный обработчик, применяемый в случаях с вложенными функциями
Что делает SEND_VAR?
SEND_VAR проверяет, является ли переменная ссылкой. Если да, то он её отделяет, тем самым создавая несоответствие ссылки. Почему это очень плохо, вы можете почитать в другой моей статье. Затем SEND_VAR добавляет количество ссылок на нее (ссылка здесь – это не ссылка в терминах PHP, то есть не та которая &, а просто показатель того, сколько кто использует это значение) — к переменной и отправляет в стек виртуальной машины:
Каждый раз, вызывая функцию, вы увеличиваете на единицу refcount каждого переменного аргумента стека. Это происходит потому, что на переменную будет ссылаться не код функции, а её стек. Отправка переменной в стек слабо влияет на производительность, но стек занимает память. Он размещается в ней во время исполнения, но его размер высчитывается во время компиляции. После того как мы отправили в стек переменную, запускаем DO_FCALL. Ниже — пример того, какое количество кода и проверок используется только для того, чтобы мы считали вызовы PHP-функций «медленными» операторами (slow statement):
Как видите, здесь проведены небольшие проверки и использованы различные кэши. Например, указатель обработчика нашёл самый первый вызов, а потом был закэширован в главный кадр виртуальной машины, чтобы каждый последующий вызов смог использовать этот указатель.
Далее мы вызываем zend_do_fcall_common_helper(). Я не буду выкладывать здесь код этой функции, он слишком объёмный. Я покажу только те операции, которые там были выполнены. Если вкратце, это множество различных проверок, сделанных во время исполнения. PHP — динамический язык, во время выполнения он может объявлять новые функции и классы, попутно автоматически загружая файлы. Поэтому PHP вынужден проводить во время выполнения множество проверок, что плохо сказывается на производительности. Но от этого никуда не деться.
Видите, сколько проверок? Идём дальше:
Вы знаете, что каждое тело функции имеет собственную область видимости переменной. Движок переключает таблицы видимости перед вызовом кода функции, так что если он запросит переменную, то она будет найдена в соответствующей таблице. А поскольку функции и методы по сути одно и то же, можете почитать о том, как забиндить на метод указатель $this.
Как я уже говорил выше, внутренние функции (являющиеся частью С) имеют другой путь выполнения, не такой, как у пользовательских функций. Обычно он короче и лучше оптимизирован, потому что мы можем сообщить движку информацию о внутренних функциях, чего нельзя сказать о пользовательских.
Приведённая строка вызывает обработчик внутренней функции. В случае с нашим примером относительно strlen() данная строка вызовет код:
Что делает strlen()? Он извлекает аргумент из стека с помощью zend_parse_parameters(). Это «медленная» функция, потому что ей приходится поднимать стек и конвертировать аргумент в ожидаемый функцией тип (в нашем случае — в строковый). Поэтому независимо от того, что вы передадите в стек для strlen(), ей может понадобиться конвертировать аргумент, а это не самый лёгкий процесс с точки зрения производительности. Исходный код zend_parse_parameters() дает неплохое представление о том, сколько операций приходится выполнить процессору во время извлечения аргументов из стекового кадра функции.
Переходим к следующему шагу. Мы только что выполнили код тела функции, теперь нам нужно «прибраться». Начнём с восстановления области видимости:
Затем очистим стек:
Теперь вы можете представить, сколько времени тратит компьютер на вызов «очень маленькой и простой» функции strlen(). А поскольку она вызывается многократно, увеличьте это время, скажем, в 25 000 раз. Вот так микро- и миллисекунды превращаются в полноценные секунды… Обратите внимание, что я продемонстрировал только самые важные для нас инструкции во время каждого вызова PHP-функции. После этого случается ещё много всего интересного. Также имейте в виду, что в случае с strlen() «полезную работу» выполняет лишь одна строка, а сопутствующие процедуры по подготовке вызова функции по объёму больше, чем «полезная» часть кода. Однако в большинстве случаев собственный код функций всё же больше влияет на производительность, чем «вспомогательный» код движка.
Та часть кода PHP, которая относится к вызову функций, в PHP 7 была переработана с целью улучшения производительности. Однако это далеко не конец, и исходный код PHP ещё не раз будет оптимизироваться с каждым новым релизом. Не были забыты и более старые версии, вызовы функций были оптимизированы и в версиях от 5.3 до 5.5. Например, в версиях с 5.4 до 5.5 был изменён способ вычисления и создания стекового кадра (с сохранением совместимости). Ради интереса можете сравнить изменения в исполняющем модуле и способе вызова функций, сделанные в версии 5.5 по сравнению с 5.4.
Хочу подчеркнуть: все вышесказанное не значит, что PHP плох. Этот язык развивается уже 20 лет, над его исходным кодом работало множество очень талантливых программистов. За этот период он много раз перерабатывался, оптимизировался и улучшался. Доказательством этого служит тот факт, что вы сегодня используете PHP и он демонстрирует хорошую общую производительность в самых разных проектах.
Это не функция, круглые скобки не обязательно означают «вызов функции». isset() включён в специальный opcode виртуальной машины Zend (ISSET_ISEMPTY), который не инициализирует вызов функции и не подвергается связанным с этим задержкам. Поскольку isset() может использовать параметры нескольких типов, его код в виртуальной машине Zend получается довольно длинным. Но если оставить только часть, относящуюся к параметру offset, то получится примерно так:
Если убрать многочисленные точки принятия решения (конструкции if), то «основной» вычислительный алгоритм можно выразить строкой:
Если offset больше нуля (вы не имели в виду isset($a[-42])) и строго меньше длины строки, результат будет принят равным 1. Тогда итогом операции будет булево TRUE. Не волнуйтесь насчёт вычисления длины, Z_STRLEN_P(container) ничего не вычисляет. Помните, что PHP уже известна длина вашей строки. Z_STRLEN_P(container) просто считывает это значение в память, на что тратится крайне мало ресурсов процессора.
Теперь вы понимаете, почему с точки зрения использования смещения строки обработка вызова функции strlen() требует ГОРАЗДО больше вычислительных ресурсов, чем обработка isset(). Последний существенно «легче». Пусть вас не пугает большое количество условных операторов if, это не самая тяжёлая часть С-кода. К тому же их можно оптимизировать с помощью С-компилятора. Код обработчика isset() не ищет в хэш-таблицах, не производит сложных проверок, не присваивает указателя одному из стековых кадров, чтобы позднее достать его. Код гораздо легче, чем общий код вызова функции, и намного реже обращается к памяти (это самый важный момент). И если закольцевать многократное выполнение такой строки, можно добиться большого улучшения производительности. Конечно, результаты одной итерации strlen() и isset() будут мало отличаться — примерно на 5 мс. Но если провести 50 000 итераций…
Также обратите внимание, что у isset() и empty() практически одинаковый исходный код. В случае со смещением строки empty() будет отличаться от isset() только дополнительным чтением, если первый символ строки не является 0. Поскольку коды empty() и isset() почти не отличаются друг от друга, то empty() будет демонстрировать такую же производительность, что и isset() (учитывая, что оба используются с одинаковыми параметрами).
Если кратко — ничем.
OPCache оптимизирует код. Об этом можно почитать в презентации. Часто спрашивают, можно ли добавить оптимизационный проход, при котором strlen() переключается в isset(). Нет, это невозможно.
Оптимизационные проходы OPCache осуществляются в OPArray до того, как он помещается в совместно используемую память. Это происходит во время компиляции, а не во время выполнения. Откуда нам знать во время компиляции, что переменная, которая передаётся в strlen(), является строкой? Это известная проблема PHP, и она отчасти решается с помощью HHVM/Hack. Если бы мы записали в PHP наши переменные со строгой типизацией, то во время проходов компилятора можно было бы оптимизировать гораздо больше вещей (как и в виртуальной машине). Так как PHP является динамическим языком, во время компиляции не известно почти ничего. OPCache может оптимизировать только статические вещи, известные к моменту начала компиляции. Например, вот это:
Во время компиляции известно, что длина строки «foo» не больше 8, поэтому можно выкинуть все opcode if(), а от конструкции if оставить только часть с else.
Но что такое $a? Оно вообще существует? Это строка? К моменту прохода оптимизатора мы ещё не можем ответить на все эти вопросы — это задача для исполняющего модуля виртуальной машины. Во время компиляции мы обрабатываем абстрактную структуру, а тип и нужный объём памяти будут известны во время выполнения.
OPCache оптимизирует многие вещи, но из-за самой природы PHP он не может оптимизировать всё подряд. По крайне мере не столько, сколько в компиляторе Java или С. Увы, PHP никогда не будет языком со строгой типизацией. Также периодически высказываются предложения по введению в декларирование свойств класса указания read-only:
Если не касаться функциональности, подобные предложения имеют смысл с точки зрения оптимизации производительности. Когда мы компилируем подобный класс, нам известно значение $a. Мы знаем, что оно не изменится, поэтому его можно где-нибудь хранить, использовать кэшированный указатель и оптимизировать каждую итерацию обращения к подобной переменной при проходе компилятора или OPCache. Здесь основная мысль в том, что чем больше информации о типе и использовании ваших переменных или функций вы можете дать компилятору, тем больше OPCache сможет оптимизировать, тем ближе будет результат к тому, что требуется процессору.
Первым делом рекомендую не менять бездумно свой код в тех местах, где он вам кажется по-настоящему хорошим. Профилируйте и смотрите результаты. Благодаря профайлерам вроде Blackfire вы сможете сразу увидеть процесс исполнения своего скрипта, поскольку автоматически отбрасывается вся нерелевантная информация, мешающая воспринимать суть. Вы поймёте, с чего начинать работу. Ведь ваш труд стоит денег, и его тоже необходимо оптимизировать. Это хороший баланс между средствами, которые вы потратите на оптимизацию скрипта, и суммой, которую вы сэкономите, снизив расходы на содержание облака.
Вторая подсказка: PHP работает действительно быстро, эффективно и надёжно. Возможностей оптимизации PHP-скриптов не так много — например, их меньше, чем в более низкоуровневых языках вроде С. Поэтому усилия по оптимизации нужно направлять на циклы без конкретных условий на выход из них. Если профайлер покажет узкое место скрипта, вероятнее всего, оно будет внутри цикла. Именно здесь крохотные задержки накапливаются в полновесные секунды, поскольку количество итераций в циклах измеряется десятками тысяч. В PHP такие циклы одинаковы, за исключением foreach(), и ведут к одному и тому же opcode. Менять в них while на for бессмысленно, и профайлер вам это докажет.
Что касается вызовов функций, есть маленькие хитрости, с помощью которых можно предотвратить исполнение некоторых вызовов, потому что информация доступна где-то ещё.
Приведённые примеры не всегда полностью эквивалентны, за подробностями обратитесь к документации.
Да, и старайтесь избегать таких глупостей, как:
Всегда критически подходите к оптимизации, не делайте её только потому, что вы где-то что-то услышали или вам это кто-то посоветовал. Не подчиняйте структуру своего приложения достижению максимальной производительности, лучше работайте над этим в рамках отдельных возможностей вашего детища.
Чаще профилируйте свой скрипт и проверяйте каждое предположение, не следуйте слепо чужим инструкциям. Всегда всё проверяйте.
Разработчики профайлера Blackfire заложили в свой продукт механизм сбора интересных метрик, способных помочь пользователям в их работе. Регистрируется множество параметров (хотя GUI показывает ещё не все): например, когда запускается сборщик мусора, что он делает, сколько объектов создано/уничтожено в функциях, сколько несоответствий ссылок было создано во время вызовов функций, время сериализации, неправильное поведение foreach() и т.д. и т.п.
Вы, ребята, знаете, как я могу определить, из какого файла была вызвана функция внутри этой функции?
Я думал об использовании debug_backtrace .. но это не выглядит элегантным способом сделать это, и они также перечисляют другие причины в другом вопросе здесь
Так какие еще есть альтернативы?
Изнутри функции debug_backtrace() - единственный способ определить вызывающего, если, конечно, вы не передадите эту информацию как параметр.
Обратите внимание, что вы не можете использовать для этого значения по умолчанию. Например.:
__FUNCTION__ будет оцениваться во время синтаксического анализа, поэтому всегда будет иметь одно и то же значение. Функция, в которой объявлена область видимости f .
Я позаимствовал этот код некоторое время назад откуда-то, и он работает как шарм, используя debug_backtrace (). Надеюсь, вы найдете ее полезной:
Ответы команды Микки Мауса типичны для онлайн-сообщества PHP! Вместо того, чтобы делиться ответом, они спрашивают, зачем вам это нужно. Не обращайте внимания на тот факт, что solomongaby задает правильный вопрос, и это вполне нормальная функция, которую можно иметь в стандартной среде IDE и более профессиональных языках, таких как Java и Objective-C.
Соломонгаби, это самый простой способ получить то, что тебе нужно:
У вас не будет много вариантов. Другой вариант (который был опубликован в другом вопросе), чтобы заставить вызывающего абонента предоставить свою информацию с помощью вызова функции (с использованием магических констант PHP):
Была бы возможной альтернативой. Но это
- запутывать ваш код
- можно "манипулировать", что может привести к сбою кода callFunction , и будет довольно сложно отследить эти ошибки.
Если бы я был на вашем месте, я бы попытался избежать этого в функции, которая используется во всем вашем коде. используйте debug_backtrace (даже если он может быть "медленным"). Читаемый код побеждает быстрый код.
Если это необходимо для отладки, вы можете создать исключение и распечатать стек вызовов функций там, где вы его поймаете.
debug_print_backtrace() выводит стек вызовов функций. Выводит вызовы функций, имена включённых/требуемых файлов и другую информацию из функций ( eval() ).
Список параметров
Аргумент является битовой маской для следующих настроек:
DEBUG_BACKTRACE_IGNORE_ARGS | Нужно ли исключить ключ "args", то есть списки аргументов всех функций/методов, чтобы уменьшить расход памяти. |
Аргумент используется для ограничения количества вызовов функций, которые будут выведены. По умолчанию ( limit = 0 ) будет выведен весь стек вызовов.
Возвращаемые значения
Функция не возвращает значения после выполнения.
Примеры
function c () debug_print_backtrace ();
>
Результатом выполнения данного примера будет что-то подобное:
Смотрите также
User Contributed Notes 5 notes
Another way to manipulate and print a backtrace, without using output buffering:
// print backtrace, getting rid of repeated absolute path on each file
$e = new Exception ();
print_r ( str_replace ( '/path/to/code/' , '' , $e -> getTraceAsString ()));
?>
I like the output of debug_print_backtrace() but I sometimes want it as a string.
bortuzar's solution to use output buffering is great, but I'd like to factorize that into a function. Doing that however always results in whatever function name I use appearing at the top of the stack which is redundant.
Below is my noddy (simple) solution. If you don't care for renumbering the call stack, omit the second preg_replace().
function debug_string_backtrace () <
ob_start ();
debug_print_backtrace ();
$trace = ob_get_contents ();
ob_end_clean ();
This code will give you a simple horizontal stack trace to assist debugging:
$c =new C ();
$c -> test ();
echo debug_print_backtrace ();
?>
When run you get
Class C calling B.testA
*Class testB.testA ---- Y
*[line45]C.test>[line40]B.testA
Here's a function that returns a string with the same information shown in debug_print_backtrace(), with the option to exclude a certain amount of traces (by altering the $traces_to_ignore argument).
I've done a couple of tests to ensure that it prints exactly the same information, but I might have missed something.
This solution is a nice workaround to get the debug_print_backtrace() information if you're already using ob_start() in your PHP code.
function get_debug_print_backtrace ( $traces_to_ignore = 1 ) $traces = debug_backtrace ();
$ret = array();
foreach( $traces as $i => $call ) if ( $i < $traces_to_ignore ) continue;
>
$object = '' ;
if (isset( $call [ 'class' ])) $object = $call [ 'class' ]. $call [ 'type' ];
if ( is_array ( $call [ 'args' ])) foreach ( $call [ 'args' ] as & $arg ) get_arg ( $arg );
>
>
>
return implode ( "\n" , $ret );
>
function get_arg (& $arg ) if ( is_object ( $arg )) $arr = (array) $arg ;
$args = array();
foreach( $arr as $key => $value ) if ( strpos ( $key , chr ( 0 )) !== false ) $key = '' ; // Private variable found
>
$args [] = '[' . $key . '] => ' . get_arg ( $value );
>
$arg = get_class ( $arg ) . ' Object (' . implode ( ',' , $args ). ')' ;
>
>
?>
If your show your error messages in HTML (with suitable safety using entities), this function won't work nicely because it uses newlines for formatting.
Here is a function that works similarly, but using tags. Insert it near the beginning of your program to add a stack to Warning output only, or modify it as you like:
// Here is code for error stack output in HTML:
function error_handler_callback($errno,$message,$file,$line,$context)
if ($errno === E_WARNING)
echo "Stack, innermost first:
".nl2br((new Exception())->getTraceAsString());
return false; // to execute the regular error handler
>
set_error_handler("error_handler_callback");
Возвращает имя класса, из которого был вызван статический метод.
Список параметров
У этой функции нет параметров.
Возвращаемые значения
Возвращает имя класса. Возвращает false , если было вызвано вне класса.
Примеры
class foo static public function test () var_dump ( get_called_class ());
>
>
class bar extends foo >
foo :: test ();
bar :: test ();
Результат выполнения данного примера:
Смотрите также
- get_parent_class() - Возвращает имя родительского класса для объекта или класса
- get_class() - Возвращает имя класса, к которому принадлежит объект
- is_subclass_of() - Проверяет, содержит ли объект в своём дереве предков указанный класс либо прямо реализует его
User Contributed Notes 9 notes
As of PHP 5.5 you can also use "static::class" to get the name of the called class.
class Bar public static function test () var_dump (static::class);
>
>
class Foo extends Bar
string(3) "Foo"
string(3) "Bar"
get_called_class() in closure-scopes:
ABSTRACT CLASS Base
protected static $stub = [ 'baz' ];
//final public function boot()
static public function boot ()
print __METHOD__ . '-> ' . get_called_class (). PHP_EOL ;
array_walk (static:: $stub , function()
print __METHOD__ . '-> ' . get_called_class (). PHP_EOL ;
>);
>
public function __construct ()
self :: boot ();
print __METHOD__ . '-> ' . get_called_class (). PHP_EOL ;
array_walk (static:: $stub , function()
print __METHOD__ . '-> ' . get_called_class (). PHP_EOL ;
>);
>
>
CLASS Sub EXTENDS Base
>
// static boot
Base :: boot (); print PHP_EOL ;
// Base::boot -> Base
// Base:: -> Base
Sub :: boot (); print PHP_EOL ;
// Base::boot -> Sub
// Base:: -> Base
new sub ;
// Base::boot -> Sub
// Base:: -> Base
// Base->__construct -> Sub
// Base-> -> Sub
// instance boot
new sub ;
// Base->boot -> Sub
// Base-> -> Sub
// Base->__construct -> Sub
// Base-> -> Sub
?>
I think it is worth mentioning on this page, that many uses of the value returned by get_called_function() could be handled with the new use of the old keyword static, as in
static:: $foo ;
?>
versus
$that = get_called_class ();
$that :: $foo ;
?>
I had been using $that:: as my conventional replacement for self:: until my googling landed me the url above. I have replaced all uses of $that with static with success both as
static:: $foo ; //and.
new static();
?>
Since static:: is listed with the limitation: "Another difference is that static:: can only refer to static properties." one may still need to use a $that:: to call static functions; though I have not yet needed this semantic.
namespace root;
class Factor protected static $instance = null;
private function __construct()
public static function getInstance() if (!self::$instance) $name = get_called_class();
self::$instance = new $name();
>
class Single extends Factor public function abc() return 'abc';
>
>
class Index public function get() return Single::getInstance();
>
>
$index = new Index();
var_dump($index->get());
It is possible to write a completely self-contained Singleton base class in PHP 5.3 using get_called_class.
abstract class Singleton
protected function __construct () <
>
final public static function getInstance () <
static $aoInstance = array();
if (! isset ( $aoInstance [ $calledClassName ])) <
$aoInstance [ $calledClassName ] = new $calledClassName ();
>
return $aoInstance [ $calledClassName ];
>
final private function __clone () <
>
>
class DatabaseConnection extends Singleton
protected function __construct () <
// @todo Connect to the database
>
public function __destruct () <
// @todo Drop the connection to the database
>
>
$oDbConn = new DatabaseConnection (); // Fatal error
$oDbConn = DatabaseConnection :: getInstance (); // Returns single instance
?>
When calling dynamic method statically in php 5.6 (under 7) it allows it, but it doesnt work, it incorrectly evaluates class that called our subject class, therefore containing method must be static.
If you call a static getInstance() function to create a instance of a class from another class, this function have to be static, if it is not static the original name of the caller class and not of the current class get returned.
class a function getXName () return x :: getClassName ();
>
function getXStaticName () return x :: getStaticClassName ();
>
class b extends a >
class x public function getClassName () return get_called_class ();
>
public static function getStaticClassName () return get_called_class ();
>
>
echo $a -> getXName (); // will return "a"
echo $b -> getXName (); // will return "b"
echo $a -> getXStaticName (); // will return "x"
echo $b -> getXStaticName (); // will return "x"
Beware that this does not behave as expected if your method is not declared as static! For example:
class foo static public function test () var_dump ( get_called_class ());
>
public function testTwo () var_dump ( get_called_class ());
>
>
class bar extends foo >
class abc function test () foo :: test ();
bar :: test ();
>
function testTwo () foo :: testTwo ();
bar :: testTwo ();
>
>
echo "basic\n" ;
foo :: test ();
bar :: test ();
echo "basic without static declaration\n" ;
foo :: testTwo ();
bar :: testTwo ();
echo "in a class\n" ;
$abc = new abc ();
$abc -> test ();
echo "in a class without static declaration\n" ;
$abc -> testTwo ();
basic
string 'foo'
string 'bar'
basic without static declaration
string 'foo'
string 'bar'
in a class
string 'foo'
string 'bar'
in a class without static declaration
string 'abc'
string 'abc'
Here's a simple way of getting the inheritance tree of a class, no matter which class the function was actually defined in. Will work as a static function method too.
class A <
public function get_class_tree () <
$cur_class = get_called_class ();
do <
echo $cur_class ;
>
while( $cur_class = get_parent_class ( $cur_class ));
>
>
Читайте также: