@@ -1341,6 +1341,44 @@ struct _phar_t {
13411341 int count ;
13421342};
13431343
1344+ static zend_always_inline void phar_call_method_with_unwrap (zend_object * obj , const char * name , zval * rv )
1345+ {
1346+ zend_call_method_with_0_params (obj , obj -> ce , NULL , name , rv );
1347+ if (Z_ISREF_P (rv )) {
1348+ zend_unwrap_reference (rv );
1349+ }
1350+ }
1351+
1352+ /* This is the same as phar_get_or_create_entry_data(), but allows overriding metadata via SplFileInfo. */
1353+ static phar_entry_data * phar_build_entry_data (char * fname , size_t fname_len , char * path , size_t path_len , char * * error , zval * file_info )
1354+ {
1355+ uint32_t timestamp ;
1356+
1357+ /* Expects an instance of SplFileInfo if it is an object, which is verified in phar_build(). */
1358+ if (Z_TYPE_P (file_info ) == IS_OBJECT && Z_OBJCE_P (file_info )-> type == ZEND_USER_CLASS ) {
1359+ zval rv ;
1360+ phar_call_method_with_unwrap (Z_OBJ_P (file_info ), "getMTime" , & rv );
1361+
1362+ if (UNEXPECTED (Z_TYPE (rv ) != IS_LONG )) {
1363+ /* Either it's a tentative type failure, an exception happened, or the function returned false to indicate failure. */
1364+ * error = estrdup ("getMTime() must return an int" );
1365+ return NULL ;
1366+ }
1367+
1368+ /* Sanity check bounds. See GH-14141. */
1369+ if (ZEND_LONG_UINT_OVFL (Z_LVAL (rv ))) {
1370+ * error = estrdup ("timestamp is limited to 32-bit" );
1371+ return NULL ;
1372+ }
1373+
1374+ timestamp = Z_LVAL (rv );
1375+ } else {
1376+ timestamp = time (NULL );
1377+ }
1378+
1379+ return phar_get_or_create_entry_data (fname , fname_len , path , path_len , "w+b" , 0 , error , true, timestamp );
1380+ }
1381+
13441382static int phar_build (zend_object_iterator * iter , void * puser ) /* {{{ */
13451383{
13461384 zval * value ;
@@ -1351,7 +1389,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
13511389 php_stream * fp ;
13521390 size_t fname_len ;
13531391 size_t contents_len ;
1354- char * fname , * error = NULL , * base = ZSTR_VAL (p_obj -> base ), * save = NULL , * temp = NULL ;
1392+ char * fname = NULL , * error = NULL , * base = ZSTR_VAL (p_obj -> base ), * save = NULL , * temp = NULL ;
13551393 zend_string * opened ;
13561394 char * str_key ;
13571395 zend_class_entry * ce = p_obj -> c ;
@@ -1418,26 +1456,49 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
14181456 return ZEND_HASH_APPLY_STOP ;
14191457 }
14201458
1421- switch (intern -> type ) {
1422- case SPL_FS_DIR : {
1423- char * tmp ;
1424- zend_string * test_str = spl_filesystem_object_get_path (intern );
1425- fname_len = spprintf (& tmp , 0 , "%s%c%s" , ZSTR_VAL (test_str ), DEFAULT_SLASH , intern -> u .dir .entry .d_name );
1426- zend_string_release_ex (test_str , /* persistent */ false);
1427- if (php_stream_stat_path (tmp , & ssb ) == 0 && S_ISDIR (ssb .sb .st_mode )) {
1428- /* ignore directories */
1429- efree (tmp );
1430- return ZEND_HASH_APPLY_KEEP ;
1459+ zend_string * tmp_dir_str = NULL ;
1460+
1461+ /* Take into account that SplFileObject may be overridden.
1462+ * The purpose here is to grab the path name of the file to add. */
1463+ if (Z_OBJCE_P (value )-> type == ZEND_USER_CLASS ) {
1464+ zval rv ;
1465+ phar_call_method_with_unwrap (Z_OBJ_P (value ), "getPathname" , & rv );
1466+
1467+ if (UNEXPECTED (Z_TYPE (rv ) != IS_STRING )) {
1468+ zend_throw_exception_ex (spl_ce_UnexpectedValueException , 0 , "getPathname() must return a string" );
1469+ return ZEND_HASH_APPLY_STOP ;
1470+ }
1471+ tmp_dir_str = Z_STR (rv );
1472+ } else {
1473+ /* Not a user class, so we can grab the data internally quickly. */
1474+ switch (intern -> type ) {
1475+ case SPL_FS_DIR : {
1476+ zend_string * test_str = spl_filesystem_object_get_path (intern );
1477+ const char slash = DEFAULT_SLASH ;
1478+ tmp_dir_str = zend_string_concat3 (
1479+ ZSTR_VAL (test_str ), ZSTR_LEN (test_str ),
1480+ & slash , 1 ,
1481+ intern -> u .dir .entry .d_name , strlen (intern -> u .dir .entry .d_name )
1482+ );
1483+ zend_string_release_ex (test_str , /* persistent */ false);
1484+ break ;
14311485 }
1486+ case SPL_FS_INFO :
1487+ case SPL_FS_FILE :
1488+ fname = expand_filepath (ZSTR_VAL (intern -> file_name ), NULL );
1489+ break ;
1490+ }
1491+ }
14321492
1433- fname = expand_filepath (tmp , NULL );
1434- efree (tmp );
1435- break ;
1493+ if (tmp_dir_str ) {
1494+ if (php_stream_stat_path (ZSTR_VAL (tmp_dir_str ), & ssb ) == 0 && S_ISDIR (ssb .sb .st_mode )) {
1495+ /* ignore directories */
1496+ zend_string_release_ex (tmp_dir_str , /* persistent */ false);
1497+ return ZEND_HASH_APPLY_KEEP ;
14361498 }
1437- case SPL_FS_INFO :
1438- case SPL_FS_FILE :
1439- fname = expand_filepath (ZSTR_VAL (intern -> file_name ), NULL );
1440- break ;
1499+
1500+ fname = expand_filepath (ZSTR_VAL (tmp_dir_str ), NULL );
1501+ zend_string_release_ex (tmp_dir_str , /* persistent */ false);
14411502 }
14421503
14431504 if (!fname ) {
@@ -1578,7 +1639,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
15781639 return ZEND_HASH_APPLY_KEEP ;
15791640 }
15801641
1581- if (!(data = phar_get_or_create_entry_data (phar_obj -> archive -> fname , phar_obj -> archive -> fname_len , str_key , str_key_len , "w+b" , 0 , & error , true ))) {
1642+ if (!(data = phar_build_entry_data (phar_obj -> archive -> fname , phar_obj -> archive -> fname_len , str_key , str_key_len , & error , value ))) {
15821643 zend_throw_exception_ex (spl_ce_BadMethodCallException , 0 , "Entry %s cannot be created: %s" , str_key , error );
15831644 efree (error );
15841645
@@ -3536,7 +3597,7 @@ static void phar_add_file(phar_archive_data **pphar, zend_string *file_name, con
35363597 }
35373598#endif
35383599
3539- if (!(data = phar_get_or_create_entry_data ((* pphar )-> fname , (* pphar )-> fname_len , filename , filename_len , "w+b" , 0 , & error , true))) {
3600+ if (!(data = phar_get_or_create_entry_data ((* pphar )-> fname , (* pphar )-> fname_len , filename , filename_len , "w+b" , 0 , & error , true, time ( NULL ) ))) {
35403601 if (error ) {
35413602 zend_throw_exception_ex (spl_ce_BadMethodCallException , 0 , "Entry %s does not exist and cannot be created: %s" , filename , error );
35423603 efree (error );
@@ -3608,7 +3669,7 @@ static void phar_mkdir(phar_archive_data **pphar, zend_string *dir_name)
36083669 char * error ;
36093670 phar_entry_data * data ;
36103671
3611- if (!(data = phar_get_or_create_entry_data ((* pphar )-> fname , (* pphar )-> fname_len , ZSTR_VAL (dir_name ), ZSTR_LEN (dir_name ), "w+b" , 2 , & error , true))) {
3672+ if (!(data = phar_get_or_create_entry_data ((* pphar )-> fname , (* pphar )-> fname_len , ZSTR_VAL (dir_name ), ZSTR_LEN (dir_name ), "w+b" , 2 , & error , true, time ( NULL ) ))) {
36123673 if (error ) {
36133674 zend_throw_exception_ex (spl_ce_BadMethodCallException , 0 , "Directory %s does not exist and cannot be created: %s" , ZSTR_VAL (dir_name ), error );
36143675 efree (error );
0 commit comments