@@ -338,77 +338,119 @@ private function label($reflexProp, $parameters, $key, $defaultLabel)
338338 }
339339
340340 /**
341- * Appends table rows based on class properties.
342- *
343- * This method generates rows for the table based on the properties of the class
344- * and appends them to the provided DOM node.
341+ * Appends table rows based on class properties using reflection and annotations.
345342 *
346343 * @param DOMDocument $doc The DOM document used to create elements.
347344 * @param DOMNode $tbody The DOM node representing the <tbody> of the table.
348- * @param array $props Array of ReflectionProperty objects representing class properties .
345+ * @param ReflectionProperty[] $props Array of ReflectionProperty objects.
349346 * @param string $className Name of the class for reflection.
350347 * @return self Returns the current instance for method chaining.
351348 */
352349 private function appendByProp ($ doc , $ tbody , $ props , $ className )
353350 {
354351 foreach ($ props as $ prop ) {
355352 $ key = $ prop ->name ;
356- $ label = $ key ;
357353 $ value = $ this ->get ($ key );
358- if (is_scalar ($ value )) {
359- $ tr = $ tbody ->appendChild ($ doc ->createElement (self ::TAG_TR ));
360-
361- $ reflexProp = new PicoAnnotationParser ($ className , $ key , PicoAnnotationParser::PROPERTY );
362354
363- if ($ reflexProp != null ) {
364- $ parameters = $ reflexProp ->getParametersAsObject ();
365- if ($ parameters ->issetLabel ()) {
366- $ label = $ this ->label ($ reflexProp , $ parameters , $ key , $ label );
355+ // Inclusion of null values often necessary for table structure consistency
356+ if (is_scalar ($ value ) || $ value === null ) {
357+ $ label = $ key ;
358+
359+ // Parse annotations for custom label
360+ try {
361+ $ reflexProp = new PicoAnnotationParser ($ className , $ key , PicoAnnotationParser::PROPERTY );
362+ if ($ reflexProp != null ) {
363+ $ parameters = $ reflexProp ->getParametersAsObject ();
364+ // Assuming issetLabel() or similar check exists in your annotation parser
365+ if (method_exists ($ parameters , 'issetLabel ' ) && $ parameters ->issetLabel ()) {
366+ $ label = $ this ->label ($ reflexProp , $ parameters , $ key , $ label );
367+ }
367368 }
369+ } catch (Exception $ e ) {
370+ // Fallback to property name if annotation parsing fails
371+ $ label = $ key ;
368372 }
369373
370- $ td1 = $ tr ->appendChild ($ doc ->createElement (self ::TAG_TD ));
374+ // Create Row
375+ $ tr = $ doc ->createElement (self ::TAG_TR );
376+ $ tbody ->appendChild ($ tr );
377+
378+ // Column 1: Label
379+ $ td1 = $ doc ->createElement (self ::TAG_TD );
371380 $ td1 ->setAttribute (self ::KEY_CLASS , self ::TD_LABEL );
372- $ td1 ->textContent = $ label ;
381+ $ td1 ->appendChild ($ doc ->createTextNode ($ label ));
382+ $ tr ->appendChild ($ td1 );
373383
374- $ td2 = $ tr ->appendChild ($ doc ->createElement (self ::TAG_TD ));
384+ // Column 2: Value
385+ $ td2 = $ doc ->createElement (self ::TAG_TD );
375386 $ td2 ->setAttribute (self ::KEY_CLASS , self ::TD_VALUE );
376- $ td2 ->textContent = isset ($ value ) ? $ value : "" ;
387+
388+ // Safe formatting for boolean/null/strings
389+ $ displayValue = $ this ->formatScalarValue ($ value );
390+ $ td2 ->appendChild ($ doc ->createTextNode ($ displayValue ));
391+ $ tr ->appendChild ($ td2 );
377392 }
378393 }
379394 return $ this ;
380395 }
381396
382- /**
397+ /**
383398 * Appends table rows based on provided values.
384399 *
385400 * This method takes an array of values and creates rows in the table,
386401 * appending them to the provided DOM node.
387402 *
388403 * @param DOMDocument $doc The DOM document used to create elements.
389404 * @param DOMNode $tbody The DOM node representing the <tbody> of the table.
390- * @param stdClass $values Data to append as rows.
405+ * @param array| stdClass $values Data to append as rows.
391406 * @return self Returns the current instance for method chaining.
392407 */
393408 private function appendByValues ($ doc , $ tbody , $ values )
394409 {
410+ if (empty ($ values )) {
411+ return $ this ;
412+ }
413+
395414 foreach ($ values as $ propertyName => $ value ) {
396- if (is_scalar ($ value )) {
397- $ tr = $ tbody ->appendChild ($ doc ->createElement (self ::TAG_TR ));
398- $ label = $ this ->getLabel ($ propertyName );
415+ // Check if value can be displayed as string
416+ if (is_scalar ($ value ) || $ value === null ) {
417+ $ tr = $ doc ->createElement (self ::TAG_TR );
418+ $ tbody ->appendChild ($ tr );
399419
400- $ td1 = $ tr ->appendChild ($ doc ->createElement (self ::TAG_TD ));
420+ $ label = $ this ->getLabel ($ propertyName );
421+
422+ // Column 1: Label
423+ $ td1 = $ doc ->createElement (self ::TAG_TD );
401424 $ td1 ->setAttribute (self ::KEY_CLASS , self ::TD_LABEL );
402- $ td1 ->textContent = $ label ;
425+ // Use createTextNode for safer character handling
426+ $ td1 ->appendChild ($ doc ->createTextNode ($ label ));
427+ $ tr ->appendChild ($ td1 );
403428
404- $ td2 = $ tr ->appendChild ($ doc ->createElement (self ::TAG_TD ));
429+ // Column 2: Value
430+ $ td2 = $ doc ->createElement (self ::TAG_TD );
405431 $ td2 ->setAttribute (self ::KEY_CLASS , self ::TD_VALUE );
406- $ td2 ->textContent = isset ($ value ) ? $ value : "" ;
432+
433+ // Format boolean or null if necessary
434+ $ displayValue = $ this ->formatScalarValue ($ value );
435+ $ td2 ->appendChild ($ doc ->createTextNode ($ displayValue ));
436+ $ tr ->appendChild ($ td2 );
407437 }
408438 }
409439 return $ this ;
410440 }
411441
442+ /**
443+ * Helper to ensure value is safe string for DOM
444+ * @param mixed $value
445+ * @return string
446+ */
447+ private function formatScalarValue ($ value )
448+ {
449+ if ($ value === true ) return 'true ' ;
450+ if ($ value === false ) return 'false ' ;
451+ return (string )(isset ($ value ) ? $ value : "" );
452+ }
453+
412454 /**
413455 * Gets the label for a specified property.
414456 *
0 commit comments