# question regarding type hinting parameters of php functions (array_slice)

- Thread: https://externals.io/message/33483
- Original archive: https://news-web.php.net/php.internals/33483
- Started by Dirk Thomas / 4wd media on 2007-11-28T10:47:29+00:00
- Messages: 19
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #33483 — Dirk Thomas / 4wd media — 2007-11-28T10:47:29+00:00

URL: https://externals.io/message/33483#33483

Hi,

i have tried a current snapshot of PHP 5.3 and have a question regarding
type hinting.

For example when using the function
"array_slice(array $array, int $offset, int $length)"
with a non-integer length parameter, what is the desired behavior?

When calling
    "array_slice($array, 0, (float)2);"
    the resulting array is EMPTY.
When using the right type
    "array_slice($array, 0, (int)2);"
    it works as expected.

Shouldn't there be a notice/warning than just a wrong return value?
In my case there is neither a warning nor does it work as expected.
(perhaps i do something wrong?)

Of course i can use int's, but in my opinion either a warning should be
given or the function should gracefully handle the wrong typed parameter.

Is this problem specific to "array_splice" or the same with other 
functions? It works with 5.2.x without a problem.

Thank you for any feedback,
Dirk

PS: i posted this to php-general before and someone supposed to better 
post it to the iternals list

---

## #33484 — Marco Kaiser — 2007-11-28T11:33:54+00:00

URL: https://externals.io/message/33483#33484 · In reply to: #33483

Hi Dirk,

> When calling
>     "array_slice($array, 0, (float)2);"
>     the resulting array is EMPTY.
> When using the right type
>     "array_slice($array, 0, (int)2);"
>     it works as expected.

i think this should print a warning like other array functions.
But i looked into the src and this looks more like a casting bug inside this
function.

$input = array('a', 'b', 'c', 'd', 'e');
var_dump(array_slice($input, 1, "1"));
var_dump(array_slice($input, 1, 1));
var_dump(array_slice($input, 1, (FLOAT)1));
var_dump(array_slice($input, 1, 1.0));

output:
array(1) {
  [0]=>
  string(1) "b"
}
array(1) {
  [0]=>
  string(1) "b"
}
array(0) {
}
array(0) {
}

So i think the float value isnt correct casted as int value here. Maybe
someone else
can proof this.

-- Marco

---

## #33485 — Moritz Bechler — 2007-11-28T13:07:31+00:00

URL: https://externals.io/message/33483#33485 · In reply to: #33484

Hi,
> 
> > […]
> 
> i think this should print a warning like other array functions.
> But i looked into the src and this looks more like a casting bug inside this
> function.

Actually most functions use implicit type conversions for this. So I
wouldn't say this should cause a warning.

> 
> 
> So i think the float value isnt correct casted as int value here. Maybe
> someone else
> can proof this.

Yes, you are right here I think....

the code is

if (ZEND_NUM_ARGS() >= 3 && Z_TYPE_P(length_param) != IS_NULL) {
	length = Z_LVAL_P(length_param);
} else {
	length = num_in;
}

and afaik should be

if (ZEND_NUM_ARGS() >= 3 && Z_TYPE_P(length_param) != IS_NULL) {
	convert_to_long(length_param);
	length = Z_LVAL_P(length_param);
} else {
	length = num_in;
}


best regards

Moritz Bechler

---

## #33486 — Stanislav Malyshev — 2007-11-28T18:48:24+00:00

URL: https://externals.io/message/33483#33486 · In reply to: #33485

> the code is
> 
> if (ZEND_NUM_ARGS() >= 3 && Z_TYPE_P(length_param) != IS_NULL) {
> 	length = Z_LVAL_P(length_param);
> } else {
> 	length = num_in;
> }
> 
> and afaik should be

I think in fact it should just parse it as long and not as zval - what 
would be any reason to parse it as zval and then convert to long anyway?
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #33888 — Steph Fox — 2007-12-10T04:18:46+00:00

URL: https://externals.io/message/33483#33888 · In reply to: #33486

Hi Stas,

I just spent most of two evenings looking at this one - so much for an easy 
fix. Read on...

> > […]
>
> I think in fact it should just parse it as long and not as zval - what 
> would be any reason to parse it as zval and then convert to long anyway?

The problem is that this function's always been wrong, so it doesn't really 
care what you throw at it - it just does a silent conversion to long if you 
get it wrong. If you turn the parameter into a long now, there's a good 
chance of breaking a lot of code out there - not least because at present if 
you give `array_slice()` a length of 0, that's what it (sanely or otherwise) 
takes it to mean. If you fix it, it will see 0 as meaning 'everything to the 
end of the array'. So one of the tests at present is:

var_dump(array_slice($sub_array, 0, 0));

and the expectation is for that to always return:

array(0) {
}

Unfortunately this is far from new behaviour.

As far as the original float issue goes: What's been happening up to now is 
that anything fed in as the third argument gets converted to a long, and now 
it doesn't. Should be easy enough to sort out you'd think, but when you try 
to do:

 /* We want all entries from offset to the end if length is not passed or is 
null */
 if (ZEND_NUM_ARGS() >= 3 && Z_TYPE_P(length_param) != IS_NULL) {
  convert_to_long_ex(&length_param);
  length = Z_LVAL_P(length_param);
 } else {
  length = num_in;
 }

all of a sudden the fourth parameter, preserve_keys, doesn't throw 
zend_parse_parameter() warnings any more, regardless of the type you give 
it. (You tell me...)

There's also the issue of what to do about inappropriate vs appropriate 
input types, since we can't rely on zend_parse_parameters() to psychically 
know that this particular zval is really a loose long kind of thing... so I 
tried:

 if (ZEND_NUM_ARGS() >= 3 && Z_TYPE_P(length_param) > IS_DOUBLE) {
  php_error_docref(NULL TSRMLS_CC, E_WARNING, "integer expected in parameter 
3, %s given", zend_zval_type_name(length_param));
 }

This actually doesn't seem to harm anything, but I bet Dmitry'd say 
otherwise (it probably slows the function right down).

So - not as straightforward as it looks, whatever way you look at it. Maybe 
we should just revert to the old, messy but mostly working code for 
 `array_slice()`?

- Steph

---

## #33890 — Steph Fox — 2007-12-10T05:07:15+00:00

URL: https://externals.io/message/33483#33890 · In reply to: #33888

> all of a sudden the fourth parameter, preserve_keys, doesn't throw 
> zend_parse_parameter() warnings any more, regardless of the type you give 
> it. (You tell me...)

... because I re-used the same variable in the test script after it had been 
converted to long.

OK, so that mystery's solved. But the rest still stands.

- Steph

---

## #33891 — Stanislav Malyshev — 2007-12-10T05:11:30+00:00

URL: https://externals.io/message/33483#33891 · In reply to: #33888

> > […]
> 
> The problem is that this function's always been wrong, so it doesn't 
> really care what you throw at it - it just does a silent conversion to 
> long if you get it wrong. If you turn the parameter into a long now, 

Right, that's what "l" parameter spec is doing, doesn't it? Why convert 
it manually?

> There's also the issue of what to do about inappropriate vs appropriate 
> input types, since we can't rely on zend_parse_parameters() to 
> psychically know that this particular zval is really a loose long kind 
> of thing... so I tried:

What's "loose long"?

> So - not as straightforward as it looks, whatever way you look at it. 
> Maybe we should just revert to the old, messy but mostly working code 
> for `array_slice()`?

I still don't understand what prevents one from using new parameter 
parsing API and why manual conversions doing the same are needed.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #33892 — Steph Fox — 2007-12-10T05:20:58+00:00

URL: https://externals.io/message/33483#33892 · In reply to: #33891

> > […]
>
> Right, that's what "l" parameter spec is doing, doesn't it? Why convert it 
> manually?

Because it doesn't know the difference between `NULL` and 0 if it's a long.

> > […]
>
> What's "loose long"?

One that doesn't break every script that didn't strictly stay with int over 
the last 8 years or so.

> > […]
>
> I still don't understand what prevents one from using new parameter 
> parsing API and why manual conversions doing the same are needed.

See above. Unless you know a way to make IS_NULL work the same way with a 
long as it does with a zval anyways.

- Steph

---

## #33893 — Stanislav Malyshev — 2007-12-10T05:40:04+00:00

URL: https://externals.io/message/33483#33893 · In reply to: #33892

> > […]
> 
> Because it doesn't know the difference between `NULL` and 0 if it's a long.

And why this difference is important in `array_slice()`? I don't see 
anything in manual that says anything about NULLs. Could you explain?

> One that doesn't break every script that didn't strictly stay with int 
> over the last 8 years or so.

Errm... I still don't understand. Could you give code example?
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #33899 — Steph Fox — 2007-12-10T16:45:27+00:00

URL: https://externals.io/message/33483#33899 · In reply to: #33893

> > […]
>
> And why this difference is important in `array_slice()`? I don't see 
> anything in manual that says anything about NULLs. Could you explain?

There's an IS_NULL check...

if (ZEND_NUM_ARGS() >= 3 && Z_TYPE_P(length_param) != IS_NULL) { ....

If Z_TYPE_P(length_param) is 0, it's not `NULL` because it has something in 
it. If length_param were of type long and had a value of 0, it would be 
NULL.

The manual kind of skirts around it:  "If length is given and is positive, 
then the sequence will have that many elements in it. If length is given and 
is negative then the sequence will stop that many elements from the end of 
the array. If it is omitted, then the sequence will have everything from 
offset up until the end of the array ."

> > […]

Actually that check hasn't been there forever, behaviour's different again 
in 5.1* - I didn't realize that. It used to always mean 0 (!) and was 
changed in 5.2.3 in response to a bug report: 
http://bugs.php.net/bug.php?id=41686.

> Errm... I still don't understand. Could you give code example?

C:\Documents and Settings\Steph\Desktop\PHP versions\5_1_2>php -a
Interactive mode enabled

<?php

$arr = array(1,2,3);
var_dump(array_slice($arr, 0, 0));
array(0) {
}
var_dump(array_slice($arr, 0, null));
array(0) {
}

C:\Documents and Settings\Steph\Desktop\PHP versions\5_2_4>php -a
Interactive mode enabled

<?php

$arr = array(1,2,3);
var_dump(array_slice($arr, 0, 0));
array(0) {
}
var_dump(array_slice($arr, 0, null));
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}

I think 0 and `NULL` should both always have acted like `NULL` does in 5_2_4 
(which would happen if the third parameter was a long), but since 0 has 
always acted wrongly it's possible that people are relying on that 
behaviour. There's also the side-issue that every type has always been 
allowed in that argument, whether it makes sense or not.

- Steph

ps the patch on my bug report is now out of date (again).


> -- 
> Stanislav Malyshev, Zend Software Architect
> stas@zend.com   http://www.zend.com/
> (408)253-8829   MSN: stas@zend.com

---

## #33902 — Stanislav Malyshev — 2007-12-10T18:02:35+00:00

URL: https://externals.io/message/33483#33902 · In reply to: #33899

> There's an IS_NULL check...
> 
> if (ZEND_NUM_ARGS() >= 3 && Z_TYPE_P(length_param) != IS_NULL) { ....
> 
> If Z_TYPE_P(length_param) is 0, it's not `NULL` because it has something 
> in it. If length_param were of type long and had a value of 0, it would 
> be NULL.

Ouch... Now I see. Is there anybody using this trick, provided it never 
was documented? This is *so* wrong.

> The manual kind of skirts around it:  "If length is given and is 
> positive, then the sequence will have that many elements in it. If 
> length is given and is negative then the sequence will stop that many 
> elements from the end of the array. If it is omitted, then the sequence 
> will have everything from offset up until the end of the array ."

Meaning, basically, -1 is the same as omitted and the same as null?
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #33903 — Steph Fox — 2007-12-10T18:50:45+00:00

URL: https://externals.io/message/33483#33903 · In reply to: #33902

> > […]
>
> Meaning, basically, -1 is the same as omitted and the same as null?

No, that works as it says in the manual. -1 is one short of all elements. 
The only thing the manual doesn't say is what it means by 'omitted', so it's 
down to whether anyone's found 0 useful when it acts as it always has. If 
they haven't, we can move to long and break any existing code that uses 
something other than an integer, but at least we'll be giving a warning 
about it. If they have, we don't have any option but to stay with the zval 
and fix it to work as it did up until last month. Warnings in that case 
would be an added extra.

- Steph

ps Sane patches attached for both the long version and the zval version 
(with no warnings). The current test for array_slice behaviour will need 
fixing whatever the decision is. Please ignore everything on the associated 
bug report..

> -- 
> Stanislav Malyshev, Zend Software Architect
> stas@zend.com   http://www.zend.com/
> (408)253-8829   MSN: stas@zend.com

---

## #33905 — Stanislav Malyshev — 2007-12-10T19:03:18+00:00

URL: https://externals.io/message/33483#33905 · In reply to: #33903

>     if (ZEND_NUM_ARGS() >= 3 && Z_TYPE_P(length_param) != IS_NULL) {
> +        convert_to_long(length_param);

Isn't convert_to_long non-separating one? I think the variable supposed 
to be separated before conversion, so convert_to_long_ex (and zval **) 
would be in place.

> +    if (length == IS_NULL) {

Did you really mean IS_NULL here?
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #33907 — Steph Fox — 2007-12-10T19:23:07+00:00

URL: https://externals.io/message/33483#33907 · In reply to: #33905

> > […]
>
> Isn't convert_to_long non-separating one? I think the variable supposed to 
> be separated before conversion, so convert_to_long_ex (and zval **) would 
> be in place.

Hm you're probably right...

  if (ZEND_NUM_ARGS() >= 3 && Z_TYPE_P(length_param) != IS_NULL) {
+  convert_to_long_ex(&length_param);
   length = Z_LVAL_P(length_param);
  } else {
   length = num_in;

- because it's a zval * these days. convert_to_long() didn't break it when I 
checked, though I probably have the length assignment wrong in the one I 
posted, on reflection. (Too many patches floating around here at present, 
sorry.)

> > […]
>
> Did you really mean IS_NULL here?

It builds without complaint and works as expected - what am I missing?

- Steph

> -- 
> Stanislav Malyshev, Zend Software Architect
> stas@zend.com   http://www.zend.com/
> (408)253-8829   MSN: stas@zend.com

---

## #33908 — Stanislav Malyshev — 2007-12-10T19:25:48+00:00

URL: https://externals.io/message/33483#33908 · In reply to: #33907

> It builds without complaint and works as expected - what am I missing?

IS_NULL is variable type. length is variable value.

-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #33911 — Steph Fox — 2007-12-10T19:35:25+00:00

URL: https://externals.io/message/33483#33911 · In reply to: #33908

> > […]
>
> IS_NULL is variable type. length is variable value.

So it's better to check for == 0? What's the difference? Is an IS_NULL check 
slower?

- Steph

> -- 
> Stanislav Malyshev, Zend Software Architect
> stas@zend.com   http://www.zend.com/
> (408)253-8829   MSN: stas@zend.com

---

## #33909 — Stanislav Malyshev — 2007-12-10T19:47:04+00:00

URL: https://externals.io/message/33483#33909 · In reply to: #33911

> So it's better to check for == 0? What's the difference? Is an IS_NULL 
> check slower?

No, it's not slower, but it makes no sense to compare variable value to 
a type constant. Even if coincidentally constant value is the same one 
you need to compare to.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #33912 — Steph Fox — 2007-12-10T20:12:44+00:00

URL: https://externals.io/message/33483#33912 · In reply to: #33909

> > […]
>
> No, it's not slower, but it makes no sense to compare variable value to a 
> type constant. Even if coincidentally constant value is the same one you 
> need to compare to.

OK, so if I clean up those patches and re-send, will one of them be applied?

(It'd be nice to have some idea which one...)

- Steph

> -- 
> Stanislav Malyshev, Zend Software Architect
> stas@zend.com   http://www.zend.com/
> (408)253-8829   MSN: stas@zend.com

---

## #33915 — Steph Fox — 2007-12-10T20:47:23+00:00

URL: https://externals.io/message/33483#33915 · In reply to: #33912

Both are now on http://bugs.php.net/bug.php?id=43541. Ignore the first of
the three, it breaks when the length param isn't passed.

I'm going offline before I say anything else stupid :)

- Steph

----- Original Message ----- 
From: "Steph Fox" <steph@zend.com>
To: "Stanislav Malyshev" <stas@zend.com>
Cc: <internals@lists.php.net>
Sent: Monday, December 10, 2007 8:12 PM
Subject: Re: [PHP-DEV] question regarding type hinting parameters of php 
functions (array_slice)


> > […]
>
> OK, so if I clean up those patches and re-send, will one of them be 
> applied?
>
> (It'd be nice to have some idea which one...)
>
> - Steph
>
> > […]
