Class: Php_process

Inherits:
Object
  • Object
show all
Defined in:
lib/php_process.rb

Overview

This class starts a PHP-process and proxies various calls to it. It also spawns proxy-objects, which can you can call like they were normal Ruby-objects.

Examples

php = Php_process.new print “PID of PHP-process: #phpphp.func(”getmypid“)n” print “Explode test: #”;“, ”1;2;3;4;5“)n”

Defined Under Namespace

Classes: Created_function, Proxy_obj

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = {}) ⇒ Php_process

Spawns various used variables, launches the process and more.

Examples

If you want debugging printed to stderr: php = Php_process.new(:debug => true)



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/php_process.rb', line 31

def initialize(args = {})
  @args = args
  @debug = @args[:debug]
  @send_count = 0
  
  @responses = Tsafe::MonHash.new
  
  @object_ids = Tsafe::MonHash.new
  @object_unset_ids = Tsafe::MonArray.new
  @objects = Wref_map.new
  
  @constant_val_cache = Tsafe::MonHash.new
  
  #Used for 'create_func'.
  @callbacks = {}
  @callbacks_count = 0
  @callbacks_mutex = Mutex.new
  
  if @args[:cmd_php]
    cmd_str = "#{@args[:cmd_php]} "
  else
    cmd_str = "/usr/bin/env php5 "
  end
  
  cmd_str << "\"#{File.dirname(__FILE__)}/php_script.php\""
  
  if RUBY_ENGINE == "jruby"
    pid, @stdin, @stdout, @stderr = IO.popen4(cmd_str)
  else
    @stdin, @stdout, @stderr = Open3.popen3(cmd_str)
  end
  
  @stdout.sync = true
  @stdin.sync = true
  
  @stdin.set_encoding("iso-8859-1:utf-8")
  @stderr.set_encoding("utf-8:iso-8859-1")
  @stdout.set_encoding("utf-8:iso-8859-1")
  
  @err_thread = Thread.new do
    begin
      @stderr.each_line do |str|
        @args[:on_err].call(str) if @args[:on_err]
        $stderr.print "Process error: #{str}" if @debug or @args[:debug_stderr]
        
        if str.match(/^PHP Fatal error: (.+)\s*/)
          @fatal = str.strip
        elsif str.match(/^Killed\s*$/)
          @fatal = "Process was killed."
        end
        
        break if (!@args and str.to_s.strip.length <= 0) or (@stderr and @stderr.closed?)
      end
    rescue => e
      $stderr.puts e.inspect
      $stderr.puts e.backtrace
    end
  end
  
  $stderr.print "Waiting for PHP-script to be ready.\n" if @debug
  started = false
  @stdout.lines do |line|
    if match = line.match(/^php_script_ready:(\d+)\n/)
      started = true
      break
    end
    
    $stderr.print "Line gotten while waiting: #{line}" if @debug
  end
  
  raise "PHP process wasnt started." if !started
  check_alive
  
  $stderr.print "PHP-script ready.\n" if @debug
  start_read_loop
  
  if block_given?
    begin
      yield(self)
    ensure
      self.destroy
    end
  end
end

Instance Attribute Details

#object_idsObject (readonly)

A hash that contains links between Ruby object IDs and the PHP object IDs. It can be read because the proxy-objects adds their data to it.



14
15
16
# File 'lib/php_process.rb', line 14

def object_ids
  @object_ids
end

Instance Method Details

#constant_val(name) ⇒ Object

Returns the value of a constant on the PHP-side.



245
246
247
248
249
250
251
252
253
# File 'lib/php_process.rb', line 245

def constant_val(name)
  const_name = name.to_s
  
  if !@constant_val_cache.key?(const_name)
    @constant_val_cache[const_name] = self.send(:type => :constant_val, :name => name)
  end
  
  return @constant_val_cache[const_name]
end

#create_func(args = {}, &block) ⇒ Object

Creates a function on the PHP-side. When the function is called, it callbacks to the Ruby-side which then can execute stuff back to PHP.

Examples

func = php.create_func do |d|

d.php.static("Gtk", "main_quit")

end

button.connect(“clicked”, func)



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/php_process.rb', line 189

def create_func(args = {}, &block)
  callback_id = nil
  func = nil
  @callbacks_mutex.synchronize do
    callback_id = @callbacks_count
    func = Php_process::Created_function.new(:php => self, :id => callback_id)
    @callbacks[callback_id] = {:block => block, :func => func, :id => callback_id}
    @callbacks_count += 1
  end
  
  raise "No callback-ID?" if !callback_id
  self.send(:type => :create_func, :callback_id => callback_id)
  
  return func
end

#destroyObject

Destroys the object closing and unsetting everything.



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/php_process.rb', line 128

def destroy
  @thread.kill if @thread
  @err_thread.kill if @err_thread
  @stdout.close if @stdout
  @stdin.close if @stdin
  @stderr.close if @stderr
  @thread = nil
  @err_thread = nil
  @fatal = nil
  @responses = nil
  @object_ids = nil
  @object_unset_ids = nil
  @send_count = nil
  @args = nil
  @debug = nil
end

#eval(eval_str) ⇒ Object

Evaluates a string containing PHP-code and returns the result.

Examples

print php.eval(“array(1 => 2);”) #=> 1=>2



154
155
156
# File 'lib/php_process.rb', line 154

def eval(eval_str)
  return self.send(:type => :eval, :eval_str => eval_str)
end

#flush_unset_ids(force = false) ⇒ Object

This flushes the unset IDs to the PHP-process and frees memory. This is automatically called if 500 IDs are waiting to be flushed. Normally you would not need or have to call this manually.

Examples

php.flush_unset_ids(true)



208
209
210
211
212
213
214
215
216
217
# File 'lib/php_process.rb', line 208

def flush_unset_ids(force = false)
  return nil if !force and @object_unset_ids.length < 500
  while @object_unset_ids.length > 0 and elements = @object_unset_ids.shift(500)
    $stderr.print "Sending unsets: #{elements}\n" if @debug
    send_real("type" => "unset_ids", "ids" => elements)
  end
  
  #Clean wref-map.
  @objects.clean
end

#func(func_name, *args) ⇒ Object

Call a function in PHP.

Examples

arr = php.func(“explode”, “;”, “1;2;3;4;5”) pid_of_php_process = php.func(“getmypid”) php.func(“require_once”, “PHPExcel.php”)



171
172
173
# File 'lib/php_process.rb', line 171

def func(func_name, *args)
  return self.send(:type => :func, :func_name => func_name, :args => parse_data(args))
end

#joinObject

Joins all the threads.



122
123
124
125
# File 'lib/php_process.rb', line 122

def join
  @thread.join if @thread
  @err_thread.join if @err_thread
end

#memory_infoObject

Returns various informations about boths sides memory in a hash.



256
257
258
259
260
261
262
263
264
265
266
# File 'lib/php_process.rb', line 256

def memory_info
  return {
    :php_info => self.send(:type => :memory_info),
    :ruby_info => {
      :responses => @responses.length,
      :objects_ids => @object_ids.length,
      :object_unset_ids => @object_unset_ids.length,
      :objects => @objects.length
    }
  }
end

#new(classname, *args) ⇒ Object

Spawns a new object from a given class with given arguments and returns it.

Examples

pe = php.new(“PHPExcel”) pe.getProperties.setCreator(“kaspernj”)



162
163
164
# File 'lib/php_process.rb', line 162

def new(classname, *args)
  return self.send(:type => :new, :class => classname, :args => parse_data(args))
end

#object_cache_infoObject

Returns various info in a hash about the object-cache on the PHP-side.



117
118
119
# File 'lib/php_process.rb', line 117

def object_cache_info
  return self.send(:type => :object_cache_info)
end

#objects_unsetter(id) ⇒ Object

This object controls which IDs should be unset on the PHP-side by being a destructor on the Ruby-side.



17
18
19
20
21
22
23
24
25
# File 'lib/php_process.rb', line 17

def objects_unsetter(id)
  obj_count_id = @object_ids[id]
  
  if @object_unset_ids.index(obj_count_id) == nil
    @object_unset_ids << obj_count_id
  end
  
  @object_ids.delete(id)
end

#parse_data(data) ⇒ Object

Parses argument-data into special hashes that can be used on the PHP-side. It is public because the proxy-objects uses it. Normally you would never use it.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/php_process.rb', line 220

def parse_data(data)
  if data.is_a?(Php_process::Proxy_obj)
    return {:type => :proxyobj, :id => data.args[:id]}
  elsif data.is_a?(Php_process::Created_function)
    return {:type => :php_process_created_function, :id => data.args[:id]}
  elsif data.is_a?(Hash)
    newhash = {}
    data.each do |key, val|
      newhash[key] = parse_data(val)
    end
    
    return newhash
  elsif data.is_a?(Array)
    newarr = []
    data.each do |val|
      newarr << parse_data(val)
    end
    
    return newarr
  else
    return data
  end
end

#send(hash) ⇒ Object

Proxies to ‘send_real’ but calls ‘flush_unset_ids’ first.



146
147
148
149
# File 'lib/php_process.rb', line 146

def send(hash)
  self.flush_unset_ids
  return send_real(hash)
end

#static(class_name, method_name, *args) ⇒ Object

Sends a call to a static method on a class with given arguments.

Examples

php.static(“Gtk”, “main_quit”)



178
179
180
# File 'lib/php_process.rb', line 178

def static(class_name, method_name, *args)
  return self.send(:type => :static_method_call, :class_name => class_name, :method_name => method_name, :args => parse_data(args))
end