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

Class Method 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)



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
115
116
117
118
119
120
121
# File 'lib/php_process.rb', line 37

def initialize(args = {})
  @args = args
  @debug = @args[:debug]
  @send_count = 0
  @send_mutex = Mutex.new
  
  @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.



20
21
22
# File 'lib/php_process.rb', line 20

def object_ids
  @object_ids
end

Class Method Details

.pathObject

Returns the path to the gem.



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

def self.path
  return File.realpath(File.dirname(__FILE__))
end

Instance Method Details

#constant_val(name) ⇒ Object

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



252
253
254
255
256
257
258
259
260
# File 'lib/php_process.rb', line 252

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)



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/php_process.rb', line 196

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.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/php_process.rb', line 135

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



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

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)



215
216
217
218
219
220
221
222
223
224
# File 'lib/php_process.rb', line 215

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”)



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

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

#joinObject

Joins all the threads.



129
130
131
132
# File 'lib/php_process.rb', line 129

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.



263
264
265
266
267
268
269
270
271
272
273
# File 'lib/php_process.rb', line 263

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”)



169
170
171
# File 'lib/php_process.rb', line 169

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.



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

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.



23
24
25
26
27
28
29
30
31
# File 'lib/php_process.rb', line 23

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.



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/php_process.rb', line 227

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.



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

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”)



185
186
187
# File 'lib/php_process.rb', line 185

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