From: Greg Beaver Date: Thu, 13 Dec 2007 00:54:15 +0000 (+0000) Subject: Phar->buildFromIterator: use spl_iterator_apply(), add 4 tests (mem leaks not yet... X-Git-Tag: RELEASE_2_0_0a1~1199 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=463731e2de55af6ef439d9b4a889faea9255ee78;p=php Phar->buildFromIterator: use spl_iterator_apply(), add 4 tests (mem leaks not yet fixed in the 4th) --- diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 3dc62a7319..cc097180d1 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -306,26 +306,130 @@ PHP_METHOD(Phar, __construct) return; \ } -/* {{{ proto Phar::buildFromIterator(Iterator iter[, string base_directory]) +static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) +{ + zval **value; + zend_uchar key_type; + ulong int_key; + struct _t { + phar_archive_object *p; + zend_class_entry *c; + char *b; + uint l; + zval *ret; + } *p_obj = (struct _t*) puser; + uint str_key_len, base_len = p_obj->l; + phar_entry_data *data; + php_stream *fp; + long contents_len; + char *fname, *error, *str_key, *base = p_obj->b, *opened; + zend_class_entry *ce = p_obj->c; + phar_archive_object *phar_obj = p_obj->p; + + iter->funcs->get_current_data(iter, &value TSRMLS_CC); + if (EG(exception)) { + return ZEND_HASH_APPLY_STOP; + } + if (!value) { + /* failure in get_current_data */ + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned no value", ce->name); + } + if (Z_TYPE_PP(value) != IS_STRING) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid value (must return a string)", ce->name); + return ZEND_HASH_APPLY_STOP; + } + if (!base_len && iter->funcs->get_current_key) { + key_type = iter->funcs->get_current_key(iter, &str_key, &str_key_len, &int_key TSRMLS_CC); + if (EG(exception)) { + return ZEND_HASH_APPLY_STOP; + } + if (key_type == HASH_KEY_IS_LONG) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid key (must return a string)", ce->name); + return ZEND_HASH_APPLY_STOP; + } + if (str_key[str_key_len - 1] == '\0') str_key_len--; + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid key (must return a string)", ce->name); + return ZEND_HASH_APPLY_STOP; + } + + fname = Z_STRVAL_PP(value); + if (base_len) { + if (strstr(fname, base)) { + str_key_len = Z_STRLEN_PP(value) - base_len; + if (str_key_len <= 0) { + return ZEND_HASH_APPLY_KEEP; + } + str_key = fname + base_len; + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a path \"%s\" that is not in the base directory \"%s\"", ce->name, fname, base); + return ZEND_HASH_APPLY_STOP; + } + } +#if PHP_MAJOR_VERSION < 6 + if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a path \"%s\" that safe mode prevents opening", ce->name, fname); + return ZEND_HASH_APPLY_STOP; + } +#endif + + if (php_check_open_basedir(fname TSRMLS_CC)) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a path \"%s\" that open_basedir prevents opening", ce->name, fname); + return ZEND_HASH_APPLY_STOP; + } + + /* try to open source file, then create internal phar file and copy contents */ + fp = php_stream_open_wrapper(fname, "rb", STREAM_MUST_SEEK|0, &opened); + if (!fp) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a file that could not be opened \"%s\"", ce->name, fname); + return ZEND_HASH_APPLY_STOP; + } + + if (!(data = phar_get_or_create_entry_data(phar_obj->arc.archive->fname, phar_obj->arc.archive->fname_len, str_key, str_key_len, "w+b", &error TSRMLS_CC))) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s cannot be created: %s", str_key, error); + efree(error); + php_stream_close(fp); + return ZEND_HASH_APPLY_STOP; + } else { + if (error) { + efree(error); + } + contents_len = php_stream_copy_to_stream(fp, data->fp, PHP_STREAM_COPY_ALL); + } + php_stream_close(fp); + + add_assoc_string(p_obj->ret, str_key, opened, 1); + + data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len; + phar_entry_delref(data TSRMLS_CC); + return ZEND_HASH_APPLY_KEEP; +} + +/* {{{ proto array Phar::buildFromIterator(Iterator iter[, string base_directory]) * Construct a phar archive from an iterator. The iterator must return a series of strings * that are full paths to files that should be added to the phar. The iterator key should * be the path that the file will have within the phar archive. * * If base directory is specified, then the key will be ignored, and instead the portion of * the current value minus the base directory will be used + * + * Returned is an array mapping phar index to actual file added */ PHP_METHOD(Phar, buildFromIterator) { - zend_object_iterator *iter = NULL; - zval *obj, **value; - char *str_key, *base, *error; - uint str_key_len, base_len = 0; - zend_uchar key_type; - ulong int_key; - zend_class_entry *ce; + zval *obj; + char *error; + uint base_len = 0; + char *base; + struct { + phar_archive_object *p; + zend_class_entry *c; + char *b; + uint l; + zval *ret; + } pass; PHAR_ARCHIVE_OBJECT(); - if (PHAR_G(readonly)) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Cannot write out phar archive, phar is read-only"); @@ -336,120 +440,19 @@ PHP_METHOD(Phar, buildFromIterator) RETURN_FALSE; } - ce = Z_OBJCE_P(obj); - - iter = ce->get_iterator(ce, obj, 0 TSRMLS_CC); - if (iter && !EG(exception)) { - obj = zend_iterator_wrap(iter TSRMLS_CC); - } else { - if (!EG(exception)) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Object of type %s did not create an Iterator", ce->name); - } - return; - } - - if (iter) { - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter TSRMLS_CC); - } - if (FAILURE == iter->funcs->valid(iter TSRMLS_CC)) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned no values", ce->name); - return; - } - do { - phar_entry_data *data; - php_stream *fp; - long contents_len; - char *fname; - iter->funcs->get_current_data(iter, &value TSRMLS_CC); - if (EG(exception)) { - break; - } - if (!value) { - /* failure in get_current_data */ - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned no value", ce->name); - } - if (Z_TYPE_PP(value) != IS_STRING) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid value (must return a string)", ce->name); - break; - } - if (!base_len && iter->funcs->get_current_key) { - key_type = iter->funcs->get_current_key(iter, &str_key, &str_key_len, &int_key TSRMLS_CC); - if (EG(exception)) { - break; - } - if (key_type == HASH_KEY_IS_LONG) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid key (must return a string)", ce->name); - break; - } - if (str_key[str_key_len - 1] == '\0') str_key_len--; - } else { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned an invalid key (must return a string)", ce->name); - break; - } - - fname = Z_STRVAL_PP(value); - if (base_len) { - if (strstr(fname, base)) { - str_key_len = Z_STRLEN_PP(value) - base_len; - if (str_key_len <= 0) { - continue; - } - str_key = fname + base_len; - } else { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a path \"%s\" that is not in the base directory \"%s\"", ce->name, fname, base); - break; - } - } -#if PHP_MAJOR_VERSION < 6 - if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a path \"%s\" that safe mode prevents opening", ce->name, fname); - break; - } -#endif - - if (php_check_open_basedir(fname TSRMLS_CC)) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a path \"%s\" that open_basedir prevents opening", ce->name, fname); - break; - } + array_init(return_value); - /* try to open source file, then create internal phar file and copy contents */ - fp = php_stream_open_wrapper(fname, "rb", STREAM_MUST_SEEK|0, NULL); - if (!fp) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %s returned a file that could not be opened \"%s\"", ce->name, fname); - break; - } + pass.c = Z_OBJCE_P(obj); + pass.p = phar_obj; + pass.b = base; + pass.l = base_len; + pass.ret = return_value; - if (!(data = phar_get_or_create_entry_data(phar_obj->arc.archive->fname, phar_obj->arc.archive->fname_len, str_key, str_key_len, "w+b", &error TSRMLS_CC))) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s cannot be created: %s", str_key, error); - efree(error); - break; - } else { - if (error) { - efree(error); - } - contents_len = php_stream_copy_to_stream(fp, data->fp, PHP_STREAM_COPY_ALL); - } - data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len; - phar_entry_delref(data TSRMLS_CC); - /* This could cause an endless loop if index becomes zero again. - * In case that ever happens we need an additional flag. */ - iter->funcs->move_forward(iter TSRMLS_CC); - if (EG(exception)) { - break; - } - if (iter->funcs->valid(iter TSRMLS_CC) == FAILURE) { - /* reached end of iteration */ - break; - } - } while (1); - if (!EG(exception)) { - phar_flush(phar_obj->arc.archive, 0, 0, &error TSRMLS_CC); - if (error) { - zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); - efree(error); - } + if (SUCCESS == spl_iterator_apply(obj, (spl_iterator_apply_func_t) phar_build, (void *) &pass)) { + phar_flush(phar_obj->arc.archive, 0, 0, &error TSRMLS_CC); + if (error) { + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); + efree(error); } } diff --git a/ext/phar/tests/phar_buildfromiterator1.phpt b/ext/phar/tests/phar_buildfromiterator1.phpt new file mode 100644 index 0000000000..655285806e --- /dev/null +++ b/ext/phar/tests/phar_buildfromiterator1.phpt @@ -0,0 +1,28 @@ +--TEST-- +Phar::buildFromIterator() readonly +--SKIPIF-- + +--INI-- +phar.require_hash=0 +phar.readonly=0 +--FILE-- +buildFromIterator(1); +} catch (Exception $e) { + var_dump(get_class($e)); + echo $e->getMessage() . "\n"; +} +?> +===DONE=== +--CLEAN-- + +--EXPECT-- +string(24) "UnexpectedValueException" +Cannot write out phar archive, phar is read-only +===DONE=== diff --git a/ext/phar/tests/phar_buildfromiterator2.phpt b/ext/phar/tests/phar_buildfromiterator2.phpt new file mode 100644 index 0000000000..44cde5ea74 --- /dev/null +++ b/ext/phar/tests/phar_buildfromiterator2.phpt @@ -0,0 +1,26 @@ +--TEST-- +Phar::buildFromIterator() wrong object +--SKIPIF-- + +--INI-- +phar.require_hash=0 +phar.readonly=0 +--FILE-- +buildFromIterator(new stdClass); +} catch (Exception $e) { + var_dump(get_class($e)); + echo $e->getMessage() . "\n"; +} +?> +===DONE=== +--CLEAN-- + +--EXPECTF-- +Warning: Phar::buildFromIterator() expects parameter 1 to be Traversable, object given in %sphar_buildfromiterator2.php on line %d +===DONE=== diff --git a/ext/phar/tests/phar_buildfromiterator3.phpt b/ext/phar/tests/phar_buildfromiterator3.phpt new file mode 100644 index 0000000000..d3bac72c0d --- /dev/null +++ b/ext/phar/tests/phar_buildfromiterator3.phpt @@ -0,0 +1,54 @@ +--TEST-- +Phar::buildFromIterator() iterator, but object passed +--SKIPIF-- + +--INI-- +phar.require_hash=0 +phar.readonly=0 +--FILE-- +a = $a; + } + function next() { + echo "next\n"; + return next($this->a); + } + function current() { + echo "current\n"; + return current($this->a); + } + function key() { + echo "key\n"; + return key($this->a); + } + function valid() { + echo "valid\n"; + return current($this->a); + } + function rewind() { + echo "rewind\n"; + return reset($this->a); + } +} +try { + $phar = new Phar(dirname(__FILE__) . '/buildfromiterator.phar'); + $phar->buildFromIterator(new myIterator(array()), new stdClass); +} catch (Exception $e) { + var_dump(get_class($e)); + echo $e->getMessage() . "\n"; +} +?> +===DONE=== +--CLEAN-- + +--EXPECTF-- +Warning: Phar::buildFromIterator() expects parameter 2 to be string, object given in %sphar_buildfromiterator3.php on line %d +===DONE=== diff --git a/ext/phar/tests/phar_buildfromiterator4.phpt b/ext/phar/tests/phar_buildfromiterator4.phpt new file mode 100644 index 0000000000..d0af185992 --- /dev/null +++ b/ext/phar/tests/phar_buildfromiterator4.phpt @@ -0,0 +1,64 @@ +--TEST-- +Phar::buildFromIterator() iterator, nothng passed in +--SKIPIF-- + +--INI-- +phar.require_hash=0 +phar.readonly=0 +--FILE-- +a = $a; + } + function next() { + echo "next\n"; + return next($this->a); + } + function current() { + echo "current\n"; + return current($this->a); + } + function key() { + echo "key\n"; + return key($this->a); + } + function valid() { + echo "valid\n"; + return current($this->a); + } + function rewind() { + echo "rewind\n"; + return reset($this->a); + } +} +try { + chdir(dirname(__FILE__)); + $phar = new Phar(dirname(__FILE__) . '/buildfromiterator.phar'); + var_dump($phar->buildFromIterator(new myIterator(array('a' => basename(__FILE__, 'php') . 'phpt')))); +} catch (Exception $e) { + var_dump(get_class($e)); + echo $e->getMessage() . "\n"; +} +?> +===DONE=== +--CLEAN-- + +--EXPECTF-- +rewind +valid +current +key +next +valid +array (1) { + ["a"] => + string(%d) "%sphar_buildfromiterator4.phpt" +} +===DONE===