7070 ZEND_PARSE_PARAMETERS_NONE(); \
7171 obj = Z_TIDY_P(ZEND_THIS); \
7272
73+ #define TIDY_FETCH_VALID_NODE \
74+ TIDY_FETCH_ONLY_OBJECT; \
75+ if (tidy_node_validate(obj) != SUCCESS) { \
76+ RETURN_THROWS(); \
77+ }
7378#define TIDY_SET_DEFAULT_CONFIG (_doc ) \
7479 if (TG(default_config) && TG(default_config)[0]) { \
7580 php_tidy_load_config(_doc, TG(default_config)); \
@@ -96,12 +101,14 @@ struct _PHPTidyDoc {
96101 TidyDoc doc ;
97102 TidyBuffer * errbuf ;
98103 uint32_t ref_count ;
104+ size_t parse_generation ;
99105 bool initialized ;
100106};
101107
102108struct _PHPTidyObj {
103109 TidyNode node ;
104110 tidy_obj_type type ;
111+ size_t node_generation ;
105112 PHPTidyDoc * ptdoc ;
106113 zend_object std ;
107114};
@@ -215,12 +222,28 @@ static zend_result php_tidy_apply_config(TidyDoc doc, const zend_string *str_str
215222 return SUCCESS ;
216223}
217224
225+ static zend_result tidy_node_validate (const PHPTidyObj * obj )
226+ {
227+ if (!obj -> ptdoc ) {
228+ zend_throw_error (NULL , "tidyNode object is not initialized" );
229+ return FAILURE ;
230+ }
231+
232+ if (obj -> node_generation != obj -> ptdoc -> parse_generation ) {
233+ zend_throw_error (NULL , "tidyNode object is no longer valid after its document was reparsed" );
234+ return FAILURE ;
235+ }
236+
237+ return SUCCESS ;
238+ }
239+
218240static void tidy_create_node_object (zval * zv , PHPTidyDoc * ptdoc , TidyNode node )
219241{
220242 object_init_ex (zv , tidy_ce_node );
221243 PHPTidyObj * newobj = Z_TIDY_P (zv );
222244 newobj -> node = node ;
223245 newobj -> type = is_node ;
246+ newobj -> node_generation = ptdoc -> parse_generation ;
224247 newobj -> ptdoc = ptdoc ;
225248 newobj -> ptdoc -> ref_count ++ ;
226249 tidy_add_node_default_properties (newobj );
@@ -378,6 +401,7 @@ static zend_object *tidy_object_new(zend_class_entry *class_type, const zend_obj
378401 intern -> ptdoc = emalloc (sizeof (PHPTidyDoc ));
379402 intern -> ptdoc -> doc = tidyCreate ();
380403 intern -> ptdoc -> ref_count = 1 ;
404+ intern -> ptdoc -> parse_generation = 0 ;
381405 intern -> ptdoc -> initialized = false;
382406 intern -> ptdoc -> errbuf = emalloc (sizeof (TidyBuffer ));
383407 tidyBufInit (intern -> ptdoc -> errbuf );
@@ -472,6 +496,9 @@ static zend_result tidy_node_cast_handler(zend_object *in, zval *out, int type)
472496
473497 case IS_STRING :
474498 obj = php_tidy_fetch_object (in );
499+ if (tidy_node_validate (obj ) != SUCCESS ) {
500+ return FAILURE ;
501+ }
475502 tidyBufInit (& buf );
476503 if (obj -> ptdoc && tidyNodeGetText (obj -> ptdoc -> doc , obj -> node , & buf )) {
477504 ZVAL_STRINGL (out , (const char * ) buf .bp , buf .size - 1 );
@@ -528,6 +555,10 @@ static void tidy_add_node_default_properties(PHPTidyObj *obj)
528555 zval attribute , children , temp ;
529556 const char * name ;
530557
558+ if (tidy_node_validate (obj ) != SUCCESS ) {
559+ return ;
560+ }
561+
531562 tidyBufInit (& buf );
532563 (void ) tidyNodeGetText (obj -> ptdoc -> doc , obj -> node , & buf );
533564
@@ -816,6 +847,7 @@ static zend_result php_tidy_parse_string(PHPTidyObj *obj, const zend_string *str
816847 obj -> ptdoc -> initialized = true;
817848
818849 tidyBufInit (& buf );
850+ obj -> ptdoc -> parse_generation ++ ;
819851 tidyBufAttach (& buf , (byte * ) ZSTR_VAL (string ), (unsigned int ) ZSTR_LEN (string ));
820852 if (tidyParseBuffer (obj -> ptdoc -> doc , & buf ) < 0 ) {
821853 php_error_docref (NULL , E_WARNING , "%s" , (const char * ) obj -> ptdoc -> errbuf -> bp );
@@ -1475,7 +1507,7 @@ PHP_FUNCTION(tidy_get_body)
14751507/* {{{ Returns true if this node has children */
14761508PHP_METHOD (tidyNode , hasChildren )
14771509{
1478- TIDY_FETCH_ONLY_OBJECT ;
1510+ TIDY_FETCH_VALID_NODE ;
14791511
14801512 RETURN_BOOL (tidyGetChild (obj -> node ));
14811513}
@@ -1484,7 +1516,7 @@ PHP_METHOD(tidyNode, hasChildren)
14841516/* {{{ Returns true if this node has siblings */
14851517PHP_METHOD (tidyNode , hasSiblings )
14861518{
1487- TIDY_FETCH_ONLY_OBJECT ;
1519+ TIDY_FETCH_VALID_NODE ;
14881520
14891521 RETURN_BOOL (obj -> node && tidyGetNext (obj -> node ));
14901522}
@@ -1493,7 +1525,7 @@ PHP_METHOD(tidyNode, hasSiblings)
14931525/* {{{ Returns true if this node represents a comment */
14941526PHP_METHOD (tidyNode , isComment )
14951527{
1496- TIDY_FETCH_ONLY_OBJECT ;
1528+ TIDY_FETCH_VALID_NODE ;
14971529
14981530 RETURN_BOOL (tidyNodeGetType (obj -> node ) == TidyNode_Comment );
14991531}
@@ -1502,7 +1534,7 @@ PHP_METHOD(tidyNode, isComment)
15021534/* {{{ Returns true if this node is part of a HTML document */
15031535PHP_METHOD (tidyNode , isHtml )
15041536{
1505- TIDY_FETCH_ONLY_OBJECT ;
1537+ TIDY_FETCH_VALID_NODE ;
15061538
15071539 switch (tidyNodeGetType (obj -> node )) {
15081540 case TidyNode_Start :
@@ -1518,7 +1550,7 @@ PHP_METHOD(tidyNode, isHtml)
15181550/* {{{ Returns true if this node represents text (no markup) */
15191551PHP_METHOD (tidyNode , isText )
15201552{
1521- TIDY_FETCH_ONLY_OBJECT ;
1553+ TIDY_FETCH_VALID_NODE ;
15221554
15231555 RETURN_BOOL (tidyNodeGetType (obj -> node ) == TidyNode_Text );
15241556}
@@ -1527,7 +1559,7 @@ PHP_METHOD(tidyNode, isText)
15271559/* {{{ Returns true if this node is JSTE */
15281560PHP_METHOD (tidyNode , isJste )
15291561{
1530- TIDY_FETCH_ONLY_OBJECT ;
1562+ TIDY_FETCH_VALID_NODE ;
15311563
15321564 RETURN_BOOL (tidyNodeGetType (obj -> node ) == TidyNode_Jste );
15331565}
@@ -1536,7 +1568,7 @@ PHP_METHOD(tidyNode, isJste)
15361568/* {{{ Returns true if this node is ASP */
15371569PHP_METHOD (tidyNode , isAsp )
15381570{
1539- TIDY_FETCH_ONLY_OBJECT ;
1571+ TIDY_FETCH_VALID_NODE ;
15401572
15411573 RETURN_BOOL (tidyNodeGetType (obj -> node ) == TidyNode_Asp );
15421574}
@@ -1545,7 +1577,7 @@ PHP_METHOD(tidyNode, isAsp)
15451577/* {{{ Returns true if this node is PHP */
15461578PHP_METHOD (tidyNode , isPhp )
15471579{
1548- TIDY_FETCH_ONLY_OBJECT ;
1580+ TIDY_FETCH_VALID_NODE ;
15491581
15501582 RETURN_BOOL (tidyNodeGetType (obj -> node ) == TidyNode_Php );
15511583}
@@ -1554,7 +1586,7 @@ PHP_METHOD(tidyNode, isPhp)
15541586/* {{{ Returns the parent node if available or NULL */
15551587PHP_METHOD (tidyNode , getParent )
15561588{
1557- TIDY_FETCH_ONLY_OBJECT ;
1589+ TIDY_FETCH_VALID_NODE ;
15581590
15591591 TidyNode parent_node = tidyGetParent (obj -> node );
15601592 if (parent_node ) {
@@ -1565,7 +1597,7 @@ PHP_METHOD(tidyNode, getParent)
15651597
15661598PHP_METHOD (tidyNode , getPreviousSibling )
15671599{
1568- TIDY_FETCH_ONLY_OBJECT ;
1600+ TIDY_FETCH_VALID_NODE ;
15691601
15701602 TidyNode previous_node = tidyGetPrev (obj -> node );
15711603 if (previous_node ) {
@@ -1575,7 +1607,7 @@ PHP_METHOD(tidyNode, getPreviousSibling)
15751607
15761608PHP_METHOD (tidyNode , getNextSibling )
15771609{
1578- TIDY_FETCH_ONLY_OBJECT ;
1610+ TIDY_FETCH_VALID_NODE ;
15791611
15801612 TidyNode next_node = tidyGetNext (obj -> node );
15811613 if (next_node ) {
0 commit comments