You can use ZipArchive::locateName() method in most cases.
So simple solution would be
$zip = new \ZipArchive();
$zip->open('ABC.zip');
if($zip->locateName('/pqr/xyz/') !== false) {
// directory exists
}
But some archives may not have directories listed in index even though they have all files with right internal paths. In this case locateName will return false for directory.
Here is a snippet to fix it. You can extend \ZipArchive or use it in your own way.
class MyZipArchive extends \ZipArchive
{
/**
* Locates first file that contains given directory
*
* @param $directory
* @return false|int
*/
public function locateDir($directory)
{
// Make sure that dir name ends with slash
$directory = rtrim($directory, '/') . '/';
return $this->locateSubPath($directory);
}
/**
* @param $subPath
* @return false|int
*/
public function locateSubPath($subPath)
{
$index = $this->locateName($subPath);
if ($index === false) {
// No full path found
for ($i = 0; $i < $this->numFiles; $i++) {
$filename = $this->getNameIndex($i);
if ($this->_strStartsWith($filename, $subPath)) {
return $i;
}
}
}
return $index;
}
/**
* Can be replaced with str_starts_with() in PHP 8
*
* @param $haystack
* @param $needle
* @return bool
*/
protected function _strStartsWith($haystack, $needle)
{
if (strlen($needle) > strlen($haystack)) {
return false;
}
return strpos($haystack, $needle) === 0;
}
}
Then
$zip = new MyZipArchive();
$zip->open('ABC.zip');
if($zip->locateDir('/pqr/xyz/') !== false) {
// directory exists
}