When dealing with arrays, the difference between the two is small enough to be considered negligible to some. Basically, isset($arr['key']) will only return true if the key is set and not empty, whereas array_key_exists('key', $arr) will return true if the key is set, even if it’s set to null. For a long time, I used array_key_exists simply because I felt it was more explicit what it did.
However recently I’ve been playing around with the ArrayAccess interface, which lets you implement classes that have indexes, like arrays. To do so, you provide four methods to set, unset, get, and test the existence of keys in the object. This lets you use an object like an array in many ways. You can access keys using $obj['key'], you can unset them using unset($obj['key']), and you can test their existence using isset($obj['key']). However you cannot use array_key_exists('key', $obj) as of PHP 5.3.0 because that function only works on arrays. Passing in an object, even one that implements ArrayAccess, will fail and generate a warning.
This leads to an odd situation where an ArrayAccess implementation acts like an array in many ways, but array_key_exists('key', $object) can return false when $object['key'] will return actual data. To me, this is enough to say that isset is preferable unless you need to differentiate between null and unset data. This way if you want to replace your arrays with “smart arrays” later you can do so without your code breaking in a strange way.
