title: SWIG and PHP7: Strange string vector behaviour
body: <p>I am writing some wrapper code using SWIG to expose my C++ functions to PHP.</p>
  
  <p><strong>my_module.i</strong></p>
  
  <pre><code>%module phpMyModule
  
  %include "exception.i"
  %include "std_string.i"
  %include "typemaps.i"
  
  // INPUT: convert PHP native array to std::vector&lt;std::string&gt;
  %typemap(in) const std::vector&lt;std::string&gt; &amp; %{
      if (Z_TYPE($input) == IS_ARRAY) {
          std::vector&lt;std::string&gt; temp2;
          $1 = &amp;temp2;
  
          HashTable *ht = Z_ARRVAL($input);
          zval *data;
          HashPosition pos;
          for (zend_hash_internal_pointer_reset_ex(ht, &amp;pos);
               (data = zend_hash_get_current_data_ex(ht, &amp;pos)) != nullptr;
               zend_hash_move_forward_ex(ht, &amp;pos)
          ) {
              convert_to_string(data);
              $1-&gt;push_back( std::string( Z_STRVAL_P(data), Z_STRLEN_P(data) ) );
          }
      }
      else SWIG_exception( SWIG_TypeError, "Type Error: Only PHP array is supported!" );
  %}
  
  %{
  #include "MyModule.h"
  %}
  
  extern int initialize_engine( const std::string&amp; script_file, const std::vector&lt;std::string&gt;&amp; input_vars );
  </code></pre>
  
  <p><strong>SWIG generated wrapper code</strong></p>
  
  <pre><code>ZEND_NAMED_FUNCTION(_wrap_initialize_engine) {
      std::string *arg1 = 0 ;
      std::vector&lt; std::string &gt; *arg2 = 0 ;
      std::string temp1 ;
      zval args[2];
      int result;
  
      SWIG_ResetError();
      if(ZEND_NUM_ARGS() != 2 || zend_get_parameters_array_ex(2, args) != SUCCESS) {
          WRONG_PARAM_COUNT;
      }
  
      convert_to_string(&amp;args[0]);
      temp1.assign(Z_STRVAL(args[0]), Z_STRLEN(args[0]));
      arg1 = &amp;temp1;
  
      if (Z_TYPE(args[1]) == IS_ARRAY) {
          std::vector&lt;std::string&gt; temp2;
          arg2 = &amp;temp2;
  
          HashTable *ht = Z_ARRVAL(args[1]);
          zval *data;
          HashPosition pos;
          for (zend_hash_internal_pointer_reset_ex(ht, &amp;pos);
               (data = zend_hash_get_current_data_ex(ht, &amp;pos)) != nullptr;
               zend_hash_move_forward_ex(ht, &amp;pos)
          ) {
              convert_to_string(data);
              arg2-&gt;push_back( std::string( Z_STRVAL_P(data), Z_STRLEN_P(data) ) );
          }
      }
      else SWIG_exception( SWIG_TypeError, "Type Error: Only PHP array is supported!" );
  
      result = (int)initialize_engine((std::string const &amp;)*arg1,(std::vector&lt; std::string &gt; const &amp;)*arg2);
  
      RETVAL_LONG(result);
  thrown:
      return;
  fail:
      SWIG_FAIL();
  }
  </code></pre>
  
  <p><strong>test.php</strong></p>
  
  <pre><code>&lt;?php
  
      require_once '&lt;path&gt;/&lt;to&gt;/phpMyModule.php';
  
      $handle = phpMyModule::initialize_engine(
          '&lt;path&gt;/&lt;to&gt;/test.script',
          ["var1", "var2", "var3"]
      );
  
      echo "Handle #1 Value: $handle\n";
      phpMyModule::terminate_engine($handle);
  
  ?&gt;
  </code></pre>
  
  <p>Basically what the above code does is to call <code>_wrap_initialize_engine()</code> with a PHP string (<code>script_file</code>) and a PHP array of variable names (<code>input_vars</code>). SWIG uses the <code>typemap</code> to convert the PHP string and array to std::string and std::vector accordingly, and then calls the real <code>initialize_engine()</code>.</p>
  
  <p>In <code>initialize_engine( const std::string&amp; script_file, const std::vector&lt;std::string&gt;&amp; input_vars )</code> I have:</p>
  
  <pre><code>std::for_each( input_vars.begin(), input_vars.end(), [&amp;]( const std::string&amp; name ) {
      std::cout &lt;&lt; "Adding " &lt;&lt; name &lt;&lt; " ..." &lt;&lt; std::endl;
      // signature is Data::addVariable( const std::string&amp; name, const VariableVector&amp; values );
      // Data::VariableVector is actually std::vector&lt;double&gt;
      data.addVariable( name, Data::VariableVector() );
  } );
  </code></pre>
  
  <p>This works. The print out is</p>
  
  <pre><code>Adding var1 ...
  Adding var2 ...
  ...
  </code></pre>
  
  <p>But if I comment out the <code>std::cout ...</code>, all calls to <code>data.addVariable()</code> will receive an empty string for the <code>name</code> argument. (I know this because inside the call I test the name against existing names and throw an error when duplicates are used. Without the <code>std::cout ...</code> I got error saying "the name '' already exist"...)</p>
  
  <p><strong>My Question</strong></p>
  
  <p>How could this happen? A <code>const std::vector&lt;std::string&gt;&amp;</code> should not be affected by whether or not I call <code>std::cout</code> on it.</p>
  
  <p>My only guess would be that std::string re-used the char* points instead of copying them? If this was the case then the real buffers are still inside PHP and may have been changed? I believe this should not be the case but just want someone with better knowledge in C++ to confirm that for me.</p>
  
  <p>But if this was not the case, then why does the above strange behaviour happen?</p>