@@ -229,6 +229,21 @@ func (s *IntegrationTestSuite) verifyPHPSymbols(functions []string, classes []st
229229 return nil
230230}
231231
232+ func (s * IntegrationTestSuite ) verifyFunctionBehavior (phpCode string , expectedOutput string ) error {
233+ s .t .Helper ()
234+
235+ output , err := s .runPHPCode (phpCode )
236+ if err != nil {
237+ return err
238+ }
239+
240+ if ! strings .Contains (output , expectedOutput ) {
241+ return fmt .Errorf ("unexpected output.\n Expected to contain: %q\n Got: %q" , expectedOutput , output )
242+ }
243+
244+ return nil
245+ }
246+
232247func TestBasicFunction (t * testing.T ) {
233248 suite := setupTest (t )
234249
@@ -268,6 +283,41 @@ func TestBasicFunction(t *testing.T) {
268283 []string {},
269284 )
270285 require .NoError (t , err , "all functions should be accessible from PHP" )
286+
287+ err = suite .verifyFunctionBehavior (`<?php
288+ $result = test_uppercase("hello world");
289+ if ($result !== "HELLO WORLD") {
290+ echo "FAIL: test_uppercase expected 'HELLO WORLD', got '$result'";
291+ exit(1);
292+ }
293+
294+ $result = test_uppercase("");
295+ if ($result !== "") {
296+ echo "FAIL: test_uppercase with empty string expected '', got '$result'";
297+ exit(1);
298+ }
299+
300+ $sum = test_add_numbers(5, 7);
301+ if ($sum !== 12) {
302+ echo "FAIL: test_add_numbers(5, 7) expected 12, got $sum";
303+ exit(1);
304+ }
305+
306+ $result = test_is_enabled(true);
307+ if ($result !== false) {
308+ echo "FAIL: test_is_enabled(true) expected false, got " . ($result ? "true" : "false");
309+ exit(1);
310+ }
311+
312+ $result = test_is_enabled(false);
313+ if ($result !== true) {
314+ echo "FAIL: test_is_enabled(false) expected true, got " . ($result ? "true" : "false");
315+ exit(1);
316+ }
317+
318+ echo "OK";
319+ ` , "OK" )
320+ require .NoError (t , err , "all function calls should work correctly" )
271321}
272322
273323func TestClassMethodsIntegration (t * testing.T ) {
@@ -292,6 +342,93 @@ func TestClassMethodsIntegration(t *testing.T) {
292342 []string {},
293343 )
294344 require .NoError (t , err , "all classes should be accessible from PHP" )
345+
346+ err = suite .verifyFunctionBehavior (`<?php
347+ $counter = new Counter();
348+ if ($counter->getValue() !== 0) {
349+ echo "FAIL: Counter initial value expected 0, got " . $counter->getValue();
350+ exit(1);
351+ }
352+
353+ $counter->increment();
354+ if ($counter->getValue() !== 1) {
355+ echo "FAIL: Counter after increment expected 1, got " . $counter->getValue();
356+ exit(1);
357+ }
358+
359+ $counter->decrement();
360+ if ($counter->getValue() !== 0) {
361+ echo "FAIL: Counter after decrement expected 0, got " . $counter->getValue();
362+ exit(1);
363+ }
364+
365+ $counter->setValue(10);
366+ if ($counter->getValue() !== 10) {
367+ echo "FAIL: Counter after setValue(10) expected 10, got " . $counter->getValue();
368+ exit(1);
369+ }
370+
371+ $newValue = $counter->addValue(5);
372+ if ($newValue !== 15) {
373+ echo "FAIL: Counter addValue(5) expected to return 15, got $newValue";
374+ exit(1);
375+ }
376+ if ($counter->getValue() !== 15) {
377+ echo "FAIL: Counter value after addValue(5) expected 15, got " . $counter->getValue();
378+ exit(1);
379+ }
380+
381+ $counter->updateWithNullable(50);
382+ if ($counter->getValue() !== 50) {
383+ echo "FAIL: Counter after updateWithNullable(50) expected 50, got " . $counter->getValue();
384+ exit(1);
385+ }
386+
387+ $counter->updateWithNullable(null);
388+ if ($counter->getValue() !== 50) {
389+ echo "FAIL: Counter after updateWithNullable(null) expected 50 (unchanged), got " . $counter->getValue();
390+ exit(1);
391+ }
392+
393+ $counter->reset();
394+ if ($counter->getValue() !== 0) {
395+ echo "FAIL: Counter after reset expected 0, got " . $counter->getValue();
396+ exit(1);
397+ }
398+
399+ $counter1 = new Counter();
400+ $counter2 = new Counter();
401+ $counter1->setValue(100);
402+ $counter2->setValue(200);
403+ if ($counter1->getValue() !== 100 || $counter2->getValue() !== 200) {
404+ echo "FAIL: Multiple Counter instances should be independent";
405+ exit(1);
406+ }
407+
408+ $holder = new StringHolder();
409+ $holder->setData("test string");
410+ if ($holder->getData() !== "test string") {
411+ echo "FAIL: StringHolder getData expected 'test string', got '" . $holder->getData() . "'";
412+ exit(1);
413+ }
414+ if ($holder->getLength() !== 11) {
415+ echo "FAIL: StringHolder getLength expected 11, got " . $holder->getLength();
416+ exit(1);
417+ }
418+
419+ $holder->setData("");
420+ if ($holder->getData() !== "") {
421+ echo "FAIL: StringHolder empty string expected '', got '" . $holder->getData() . "'";
422+ exit(1);
423+ }
424+ if ($holder->getLength() !== 0) {
425+ echo "FAIL: StringHolder empty string length expected 0, got " . $holder->getLength();
426+ exit(1);
427+ }
428+
429+ echo "OK";
430+ ` , "OK" )
431+ require .NoError (t , err , "all class methods should work correctly" )
295432}
296433
297434func TestConstants (t * testing.T ) {
@@ -319,6 +456,78 @@ func TestConstants(t *testing.T) {
319456 },
320457 )
321458 require .NoError (t , err , "all constants, functions, and classes should be accessible from PHP" )
459+
460+ err = suite .verifyFunctionBehavior (`<?php
461+ if (TEST_MAX_RETRIES !== 100) {
462+ echo "FAIL: TEST_MAX_RETRIES expected 100, got " . TEST_MAX_RETRIES;
463+ exit(1);
464+ }
465+
466+ if (TEST_API_VERSION !== "2.0.0") {
467+ echo "FAIL: TEST_API_VERSION expected '2.0.0', got '" . TEST_API_VERSION . "'";
468+ exit(1);
469+ }
470+
471+ if (TEST_ENABLED !== true) {
472+ var_dump(TEST_ENABLED);
473+ echo "FAIL: TEST_ENABLED expected true, got " . (TEST_ENABLED ? "true" : "false");
474+ exit(1);
475+ }
476+
477+ if (abs(TEST_PI - 3.14159) > 0.00001) {
478+ echo "FAIL: TEST_PI expected 3.14159, got " . TEST_PI;
479+ exit(1);
480+ }
481+
482+ if (Config::MODE_DEBUG !== 1) {
483+ echo "FAIL: Config::MODE_DEBUG expected 1, got " . Config::MODE_DEBUG;
484+ exit(1);
485+ }
486+
487+ if (Config::MODE_PRODUCTION !== 2) {
488+ echo "FAIL: Config::MODE_PRODUCTION expected 2, got " . Config::MODE_PRODUCTION;
489+ exit(1);
490+ }
491+
492+ if (Config::DEFAULT_TIMEOUT !== 30) {
493+ echo "FAIL: Config::DEFAULT_TIMEOUT expected 30, got " . Config::DEFAULT_TIMEOUT;
494+ exit(1);
495+ }
496+
497+ $config = new Config();
498+ $config->setMode(Config::MODE_DEBUG);
499+ if ($config->getMode() !== Config::MODE_DEBUG) {
500+ echo "FAIL: Config getMode expected MODE_DEBUG, got " . $config->getMode();
501+ exit(1);
502+ }
503+
504+ $result = test_with_constants(STATUS_PENDING);
505+ if ($result !== "pending") {
506+ echo "FAIL: test_with_constants(STATUS_PENDING) expected 'pending', got '$result'";
507+ exit(1);
508+ }
509+
510+ $result = test_with_constants(STATUS_PROCESSING);
511+ if ($result !== "processing") {
512+ echo "FAIL: test_with_constants(STATUS_PROCESSING) expected 'processing', got '$result'";
513+ exit(1);
514+ }
515+
516+ $result = test_with_constants(STATUS_COMPLETED);
517+ if ($result !== "completed") {
518+ echo "FAIL: test_with_constants(STATUS_COMPLETED) expected 'completed', got '$result'";
519+ exit(1);
520+ }
521+
522+ $result = test_with_constants(999);
523+ if ($result !== "unknown") {
524+ echo "FAIL: test_with_constants(999) expected 'unknown', got '$result'";
525+ exit(1);
526+ }
527+
528+ echo "OK";
529+ ` , "OK" )
530+ require .NoError (t , err , "all constants should have correct values and functions should work" )
322531}
323532
324533func TestNamespace (t * testing.T ) {
@@ -343,6 +552,71 @@ func TestNamespace(t *testing.T) {
343552 []string {`\\TestIntegration\\Extension\\NAMESPACE_VERSION` },
344553 )
345554 require .NoError (t , err , "all namespaced symbols should be accessible from PHP" )
555+
556+ err = suite .verifyFunctionBehavior (`<?php
557+ use TestIntegration\Extension;
558+
559+ if (Extension\NAMESPACE_VERSION !== "1.0.0") {
560+ echo "FAIL: NAMESPACE_VERSION expected '1.0.0', got '" . Extension\NAMESPACE_VERSION . "'";
561+ exit(1);
562+ }
563+
564+ $greeting = Extension\greet("Alice");
565+ if ($greeting !== "Hello, Alice!") {
566+ echo "FAIL: greet('Alice') expected 'Hello, Alice!', got '$greeting'";
567+ exit(1);
568+ }
569+
570+ $greeting = Extension\greet("");
571+ if ($greeting !== "Hello, !") {
572+ echo "FAIL: greet('') expected 'Hello, !', got '$greeting'";
573+ exit(1);
574+ }
575+
576+ if (Extension\Person::DEFAULT_AGE !== 18) {
577+ echo "FAIL: Person::DEFAULT_AGE expected 18, got " . Extension\Person::DEFAULT_AGE;
578+ exit(1);
579+ }
580+
581+ $person = new Extension\Person();
582+ $person->setName("Bob");
583+ $person->setAge(25);
584+
585+ if ($person->getName() !== "Bob") {
586+ echo "FAIL: Person getName expected 'Bob', got '" . $person->getName() . "'";
587+ exit(1);
588+ }
589+
590+ if ($person->getAge() !== 25) {
591+ echo "FAIL: Person getAge expected 25, got " . $person->getAge();
592+ exit(1);
593+ }
594+
595+ $person->setAge(Extension\Person::DEFAULT_AGE);
596+ if ($person->getAge() !== 18) {
597+ echo "FAIL: Person setAge(DEFAULT_AGE) expected 18, got " . $person->getAge();
598+ exit(1);
599+ }
600+
601+ $person1 = new Extension\Person();
602+ $person2 = new Extension\Person();
603+ $person1->setName("Alice");
604+ $person1->setAge(30);
605+ $person2->setName("Charlie");
606+ $person2->setAge(40);
607+
608+ if ($person1->getName() !== "Alice" || $person1->getAge() !== 30) {
609+ echo "FAIL: person1 should have independent state";
610+ exit(1);
611+ }
612+ if ($person2->getName() !== "Charlie" || $person2->getAge() !== 40) {
613+ echo "FAIL: person2 should have independent state";
614+ exit(1);
615+ }
616+
617+ echo "OK";
618+ ` , "OK" )
619+ require .NoError (t , err , "all namespaced symbols should work correctly" )
346620}
347621
348622func TestInvalidSignature (t * testing.T ) {
0 commit comments