As stated by the OP:

PHP treats all arrays as associative

it is not quite sensible (IMHO) to write a function that checks if an array is *associative*. So first thing first: what is a key in a PHP array?:

The *key* can either be an **integer** or a **string**.

That means there are 3 possible cases:

- Case 1. all keys are
**numeric** / **integers**.
- Case 2. all keys are
**strings**.
- Case 3. some keys are
**strings**, some keys are **numeric** / **integers**.

We can check each case with the following functions.

### Case 1: all keys are **numeric** / **integers**.

**Note**: *This function returns ***true** for empty arrays too.

```
//! Check whether the input is an array whose keys are all integers.
/*!
\param[in] $InputArray (array) Input array.
\return (bool) \b true iff the input is an array whose keys are all integers.
*/
function IsArrayAllKeyInt($InputArray)
{
if(!is_array($InputArray))
{
return false;
}
if(count($InputArray) <= 0)
{
return true;
}
return array_unique(array_map("is_int", array_keys($InputArray))) === array(true);
}
```

### Case 2: all keys are **strings**.

**Note**: *This function returns ***true** for empty arrays too.

```
//! Check whether the input is an array whose keys are all strings.
/*!
\param[in] $InputArray (array) Input array.
\return (bool) \b true iff the input is an array whose keys are all strings.
*/
function IsArrayAllKeyString($InputArray)
{
if(!is_array($InputArray))
{
return false;
}
if(count($InputArray) <= 0)
{
return true;
}
return array_unique(array_map("is_string", array_keys($InputArray))) === array(true);
}
```

### Case 3. some keys are **strings**, some keys are **numeric** / **integers**.

**Note**: *This function returns ***true** for empty arrays too.

```
//! Check whether the input is an array with at least one key being an integer and at least one key being a string.
/*!
\param[in] $InputArray (array) Input array.
\return (bool) \b true iff the input is an array with at least one key being an integer and at least one key being a string.
*/
function IsArraySomeKeyIntAndSomeKeyString($InputArray)
{
if(!is_array($InputArray))
{
return false;
}
if(count($InputArray) <= 0)
{
return true;
}
return count(array_unique(array_map("is_string", array_keys($InputArray)))) >= 2;
}
```

It follows that:

Now, for an array to be a *"genuine" array* that we are all accustomed to, meaning:

- Its keys are all
**numeric** / **integers**.
- Its keys are
**sequential** (i.e. increasing by step 1).
- Its keys
**start from zero**.

We can check with the following function.

### Case 3a. keys are **numeric** / **integers**, **sequential**, and **zero-based**.

**Note**: *This function returns ***true** for empty arrays too.

```
//! Check whether the input is an array whose keys are numeric, sequential, and zero-based.
/*!
\param[in] $InputArray (array) Input array.
\return (bool) \b true iff the input is an array whose keys are numeric, sequential, and zero-based.
*/
function IsArrayKeyNumericSequentialZeroBased($InputArray)
{
if(!is_array($InputArray))
{
return false;
}
if(count($InputArray) <= 0)
{
return true;
}
return array_keys($InputArray) === range(0, count($InputArray) - 1);
}
```

## Caveats / Pitfalls (or, even more peculiar facts about array keys in PHP)

### Integer keys

The keys for these arrays are **integers**:

```
array(0 => "b");
array(13 => "b");
array(-13 => "b"); // Negative integers are also integers.
array(0x1A => "b"); // Hexadecimal notation.
```

### String keys

The keys for these arrays are **strings**:

```
array("fish and chips" => "b");
array("" => "b"); // An empty string is also a string.
array("stackoverflow_email@example.com" => "b"); // Strings may contain non-alphanumeric characters.
array("stack\t\"over\"\r\nflow's cool" => "b"); // Strings may contain special characters.
array('$tα€k↔øv∈rflöw⛄' => "b"); // Strings may contain all kinds of symbols.
array("functіon" => "b"); // You think this looks fine? Think again! (see https://stackoverflow.com/q/9246051/1402846)
array("ま말轉转ДŁ" => "b"); // How about Japanese/Korean/Chinese/Russian/Polish?
array("fi\x0sh" => "b"); // Strings may contain null characters.
array(file_get_contents("https://www.google.com/images/nav_logo114.png") => "b"); // Strings may even be binary!
```

### Integer keys that look like strings

If you think the key in `array("13" => "b")`

is a *string*, **you are wrong**. From the doc here:

Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.

For example, the key for these arrays are **integers**:

```
array("13" => "b");
array("-13" => "b"); // Negative, ok.
```

But the key for these arrays are **strings**:

```
array("13." => "b");
array("+13" => "b"); // Positive, not ok.
array("-013" => "b");
array("0x1A" => "b"); // Not converted to integers even though it's a valid hexadecimal number.
array("013" => "b"); // Not converted to integers even though it's a valid octal number.
array("18446744073709551616" => "b"); // Not converted to integers as it can't fit into a 64-bit integer.
```

What's more, according to the doc,

The size of an integer is platform-dependent, although a maximum value of about two billion is the usual value (that's 32 bits signed). 64-bit platforms usually have a maximum value of about 9E18, except for Windows, which is always 32 bit. PHP does not support unsigned integers.

So the key for this array **may or may not** be an *integer* - it depends on your platform.

```
array("60000000000" => "b"); // Array key could be integer or string, it can fit into a 64-bit (but not 32-bit) integer.
```

Even worse, PHP tends to be **buggy** if the integer is near the 2^{31} = 2,147,483,648 boundary (see bug 51430, bug 52899). For example, on my local environment (PHP 5.3.8 on XAMPP 1.7.7 on Windows 7), `var_dump(array("2147483647" => "b"))`

gives

```
array(1) {
[2147483647]=>
string(1) "b"
}
```

but on this live demo on codepad (PHP 5.2.5), the same expression gives

```
array(1) {
["2147483647"]=>
string(1) "b"
}
```

So the key is an *integer* in one environment but a *string* in another, even though `2147483647`

is a valid signed 32-bit *integer*.

`if (isset($array[0]))`

, which is simple and fast. Of course, you should first be sure the array isn't empty, and you should have some knowledge on the possible contents of the array so that the method couldn't fail (like mixed numeric/associative, or non-sequential).