php_session 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 301319f11805c591578f708b2a3cc3023e3c0fe7
4
- data.tar.gz: 09259e558139f5fb3a242fc8398084178de42cfa
3
+ metadata.gz: b17d2e471e2fc62c4c3681598183b9ce7551a277
4
+ data.tar.gz: 45400c08a847f2ef18c8e0241648542fa01bc1ab
5
5
  SHA512:
6
- metadata.gz: 24532c8a7b0f755b6a60c5ae4461c898827e2254b9cd6525b4174f48fd7cdd7128970be7a4882852b61d8bc510748a1828eff43e8ca03a85a0472ee374b78a82
7
- data.tar.gz: be39ee678cb923830646b666f2f82d018036072f903367993df75d913956a50a0390225f0c65c59b869c771cff583800aef3fb0b141a95e6eee7ebf27185c590
6
+ metadata.gz: 890cec71b4f562a2ac591099a36ea7371c190403546fb216749a72dc7dfc883ff520d72b77d87295d097a446a93ad97a625457f9067954d8ccbd4efc4ae6e869
7
+ data.tar.gz: 608e598aabacdf55c0d1c834057ff1befe09ea8898e3438b9b3b4048a1a99eb76f10eb74072d401e8c4e73b301308a42bc39063443a7958a7eba3770d5f3c145
data/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # PHPSession
2
2
  [![Build Status](https://travis-ci.org/Shinpeim/ruby_php_session.png?branch=master)](https://travis-ci.org/Shinpeim/ruby_php_session)
3
3
 
4
+ ## Description
4
5
  PHPSession is a php session file reader/writer. Multibyte string and exclusive control are supported.
5
6
 
6
7
  When decoding php session data to ruby objects,
7
8
 
8
- * An associative array in PHP is mapped to a hash in ruby.
9
- * Object in PHP is mapped to Struct::ClassName in ruby.
9
+ * Associative arrays in PHP is mapped to hashes in ruby.
10
+ * Objects in PHP is mapped to instances of Struct::ClassName in ruby.
10
11
 
11
12
  When encoding ruby objects to php session data,
12
13
 
@@ -15,6 +16,35 @@ When encoding ruby objects to php session data,
15
16
  * Arrays in ruby is mapped to a associative arrays which's keys are integer in PHP.
16
17
  * Hashes in ruby is mapped to a associative arrays which's keys are string in PHP.
17
18
 
19
+ ### Multibyte support
20
+
21
+ Passing option to PHPSession.new, you can handle encodings.
22
+
23
+ Options are:
24
+
25
+ * :internal_encoding
26
+
27
+ When this value is not nil, Session decoder tries to
28
+ encode string values into this encoding.
29
+
30
+ For a instance, if your php session file written in EUC-JP and you
31
+ like to handle string as UTF-8 in Ruby, you should set :internal_encoding
32
+ as "UTF-8" and :external_encoding as "EUC-JP".
33
+
34
+ Default value is Encoding.default_internal.
35
+
36
+ * :external_encoding
37
+
38
+ This value should be same as php session file's encoding.
39
+ Encoder tries to encode string values into this encoding.
40
+
41
+ Default value is Encoding.default_external.
42
+
43
+ * :encoding_option
44
+
45
+ This value is passed to String#encode.
46
+
47
+
18
48
  ## Installation
19
49
 
20
50
  Add this line to your application's Gemfile:
@@ -31,11 +61,22 @@ Or install it yourself as:
31
61
 
32
62
  ## Usage
33
63
  # initialize
34
- session = PHPSession.new(session_file_dir, session_id)
64
+ option = {
65
+ :internal_encoding => "UTF-8", # value will be decoded as UTF-8
66
+ :external_encoding => "EUC-JP", # encoding of sesion file is EUC-JP
67
+ :encoding_option => {:undef => :replace} # passed to String#encode
68
+ }
69
+ # option's default values are
70
+ # :internal_encoding => Encoding.default_internal_encoding
71
+ # :external_encoding => Encoding.default_external_encoding
72
+ # :encoding_option => {}
73
+ session = PHPSession.new(session_file_dir, session_id, option)
35
74
 
36
75
  begin
37
76
  # load session data from file and obtain a lock
38
77
  data = session.load
78
+
79
+ data.is_a? Hash # => true
39
80
 
40
81
  # save session and release the lock
41
82
  session.commit(data)
@@ -2,15 +2,16 @@
2
2
  class PHPSession
3
3
  class Decoder
4
4
  attr_accessor :buffer, :state, :stack, :array
5
- attr_reader :encoding
5
+ attr_reader :encoding, :encoding_option
6
6
 
7
- def self.decode(string, encoding = "UTF-8")
8
- self.new(string, encoding).decode
7
+ def self.decode(string, encoding = nil, encoding_option = {})
8
+ self.new(string, encoding, encoding_option).decode
9
9
  end
10
10
 
11
- def initialize(string, encoding)
11
+ def initialize(string, encoding, encoding_option)
12
12
  @encoding = encoding
13
- @buffer = string.force_encoding("ASCII-8BIT")
13
+ @encoding_option = encoding_option
14
+ @buffer = string
14
15
  @data = {}
15
16
  @state = State::VarName
16
17
  @stack = []
@@ -172,11 +173,11 @@ class PHPSession
172
173
  length = decoder.stack.pop
173
174
  length_include_quotes = length + 3
174
175
 
175
- value_include_quotes = decoder.buffer[0, length_include_quotes]
176
+ value_include_quotes = decoder.buffer.byteslice(0, length_include_quotes)
176
177
  value = value_include_quotes.gsub(/^"/,'').gsub(/";$/, '')
177
- value.force_encoding(decoder.encoding)
178
178
 
179
- decoder.buffer = decoder.buffer[length_include_quotes .. -1]
179
+ value = value.encode(decoder.encoding, decoder.encoding_option) if decoder.encoding
180
+ decoder.buffer = decoder.buffer.byteslice(length_include_quotes .. -1)
180
181
 
181
182
  decoder.process_value(value)
182
183
  end
@@ -1,88 +1,106 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  class PHPSession
3
3
  class Encoder
4
- def self.encode(hash)
4
+ attr_reader :encoding, :encoding_option
5
+ def self.encode(hash, encoding = nil, encoding_option = {})
6
+ encoding = Encoding.default_external if encoding.nil?
7
+ self.new(encoding, encoding_option).encode(hash)
8
+ end
9
+
10
+ def initialize(encoding, encoding_option)
11
+ @encoding = encoding
12
+ @encoding_option = encoding_option
13
+ end
14
+
15
+ def encode(hash)
5
16
  serialized = hash.map do|k,v|
6
- "#{k}|#{serialize(v)}"
17
+ "#{k.to_s}|#{serialize(v)}"
7
18
  end
8
19
  serialized.join
9
20
  end
10
21
 
11
- private
12
-
13
- def self.serialize(value)
22
+ def serialize(value)
14
23
  get_serializer(value.class).serialize(value)
15
24
  end
16
25
 
17
- def self.get_serializer(klass)
26
+ private
27
+
28
+ def get_serializer(klass)
18
29
  case
19
30
  when klass <= String || klass <= Symbol
20
- StringSerializer
31
+ StringSerializer.new(self)
21
32
  when klass <= Integer
22
- IntegerSerializer
33
+ IntegerSerializer.new(self)
23
34
  when klass <= Float
24
- FloatSerializer
35
+ FloatSerializer.new(self)
25
36
  when klass <= NilClass
26
- NilSerializer
37
+ NilSerializer.new(self)
27
38
  when klass <= TrueClass || klass <= FalseClass
28
- BooleanSerializer
39
+ BooleanSerializer.new(self)
29
40
  when klass <= Hash
30
- HashSerializer
41
+ HashSerializer.new(self)
31
42
  when klass <= Array
32
- ArraySerializer
43
+ ArraySerializer.new(self)
33
44
  when klass <= Struct
34
- StructSerializer
45
+ StructSerializer.new(self)
35
46
  else
36
47
  raise Errors::EncodeError, "unsupported class:#{klass.to_s} is passed."
37
48
  end
38
49
  end
39
50
 
40
- class StringSerializer
41
- def self.serialize(value)
42
- s = value.to_s
51
+ class Serializer
52
+ def initialize(encoder)
53
+ @encoder = encoder
54
+ end
55
+ end
56
+ class StringSerializer < Serializer
57
+ def serialize(value)
58
+ value = value.to_s
59
+ # encode here for valid bytesize
60
+ s = value.encode(@encoder.encoding, @encoder.encoding_option)
43
61
  %|s:#{s.bytesize}:"#{s}";|
44
62
  end
45
63
  end
46
- class IntegerSerializer
47
- def self.serialize(value)
64
+ class IntegerSerializer < Serializer
65
+ def serialize(value)
48
66
  %|i:#{value};|
49
67
  end
50
68
  end
51
- class FloatSerializer
52
- def self.serialize(value)
69
+ class FloatSerializer < Serializer
70
+ def serialize(value)
53
71
  %|d:#{value};|
54
72
  end
55
73
  end
56
- class NilSerializer
57
- def self.serialize(value)
74
+ class NilSerializer < Serializer
75
+ def serialize(value)
58
76
  %|N;|
59
77
  end
60
78
  end
61
- class BooleanSerializer
62
- def self.serialize(value)
79
+ class BooleanSerializer < Serializer
80
+ def serialize(value)
63
81
  %|b:#{value ? 1 : 0};|
64
82
  end
65
83
  end
66
- class HashSerializer
67
- def self.serialize(value)
84
+ class HashSerializer < Serializer
85
+ def serialize(value)
68
86
  serialized_values = value.map do |k, v|
69
- [Encoder.serialize(k), Encoder.serialize(v)]
87
+ [@encoder.serialize(k), @encoder.serialize(v)]
70
88
  end
71
89
  %|a:#{value.size}:{#{serialized_values.flatten.join}}|
72
90
  end
73
91
  end
74
- class ArraySerializer
75
- def self.serialize(value)
92
+ class ArraySerializer < Serializer
93
+ def serialize(value)
76
94
  key_values = value.map.with_index{|el, i| [i, el]}
77
95
  hash = Hash[key_values]
78
- HashSerializer.serialize(hash)
96
+ HashSerializer.new(@encoder).serialize(hash)
79
97
  end
80
98
  end
81
- class StructSerializer
82
- def self.serialize(value)
99
+ class StructSerializer < Serializer
100
+ def serialize(value)
83
101
  key_values = value.members.zip(value.values)
84
102
  serialized_key_values = key_values.map do |kv|
85
- kv.map {|el| Encoder.serialize(el)}
103
+ kv.map {|el| @encoder.serialize(el)}
86
104
  end
87
105
  class_name = value.class.to_s.gsub(/^Struct::/,'')
88
106
  %|o:#{class_name.bytesize}:"#{class_name}":#{key_values.size}:{#{serialized_key_values.flatten.join}}|
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  class PHPSession
3
- VERSION = "0.0.1"
3
+ VERSION = "0.1.0"
4
4
  end
data/lib/php_session.rb CHANGED
@@ -6,7 +6,13 @@ require "php_session/encoder"
6
6
 
7
7
  class PHPSession
8
8
  attr_reader :data
9
- def initialize(session_dir, session_id)
9
+ def initialize(session_dir, session_id, option = {})
10
+ default_option = {
11
+ :internal_encoding => Encoding.default_internal,
12
+ :external_encoding => Encoding.default_external,
13
+ :encoding_option => {},
14
+ }
15
+ @option = default_option.merge(option)
10
16
  @session_dir = File.expand_path(session_dir)
11
17
  set_session_id(session_id)
12
18
 
@@ -20,7 +26,9 @@ class PHPSession
20
26
  raise PHPSession::Errors, "can't obtain lock of session file"
21
27
  end
22
28
 
23
- data = Decoder.decode(@file.read) || {}
29
+ # set internal_encoding to nil to avoid encoding conversion
30
+ @file.set_encoding(@option[:external_encoding], nil)
31
+ data = Decoder.decode(@file.read, @option[:internal_encoding], @option[:encoding_option]) || {}
24
32
  @file.rewind
25
33
  data
26
34
  end
@@ -37,7 +45,7 @@ class PHPSession
37
45
 
38
46
  def commit(data)
39
47
  @file.truncate(0)
40
- @file.write(Encoder.encode(data))
48
+ @file.write(Encoder.encode(data, @option[:external_encoding], @option[:encoding_option]))
41
49
  ensure_file_closed
42
50
  end
43
51
 
data/php_session.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Shinpei Maruyama"]
10
10
  spec.email = ["shinpeim@gmail.com"]
11
11
  spec.description = %q{php session reader/writer}
12
- spec.summary = %q{php_session is a php session file reader/writer. Multibyte string and exclusiv control is supported}
12
+ spec.summary = %q{php_session is a php session file reader/writer. Multibyte string and exclusive control is supported}
13
13
  spec.homepage = "https://github.com/Shinpeim/ruby_php_session"
14
14
  spec.license = "MIT"
15
15
 
@@ -17,6 +17,13 @@ describe PHPSession::Encoder do
17
17
  ).to eq('hoge|s:9:"テスト";')
18
18
  end
19
19
  end
20
+ context "when given multi string value with external encoding" do
21
+ it "should return 'KEY|SERIALIZED_STRING'" do
22
+ expect(
23
+ PHPSession::Encoder.encode({:hoge => "テスト🍣"}, "EUC-JP", {:undef => :replace})
24
+ ).to eq('hoge|s:7:"テスト?";'.encode('EUC-JP'))
25
+ end
26
+ end
20
27
  context "when given int value" do
21
28
  it "should return 'KEY|SERIALIZED_INT" do
22
29
  expect(
@@ -3,6 +3,56 @@ require 'spec_helper'
3
3
 
4
4
  describe PHPSession do
5
5
  describe "load" do
6
+ context "when session file encoding is utf8" do
7
+ before do
8
+ @session_file = create_dummy_session_file('key|s:13:"テスト🍺";')
9
+ end
10
+ it "should be able to load file with internal:nil, external:utf8" do
11
+ option = {
12
+ :internal_encoding => nil,
13
+ :external_encoding => "UTF-8",
14
+ }
15
+ session = PHPSession.new(@session_file[:dir_name], @session_file[:session_id], option)
16
+ begin
17
+ data = session.load
18
+ expect(data).to eq({"key" => "テスト🍺"})
19
+ ensure
20
+ session.ensure_file_closed
21
+ end
22
+ end
23
+ it "should be able to load file with internal:utf8, external:utf8" do
24
+ option = {
25
+ :internal_encoding => "UTF-8",
26
+ :external_encoding => "UTF-8",
27
+ }
28
+ session = PHPSession.new(@session_file[:dir_name], @session_file[:session_id], option)
29
+ begin
30
+ data = session.load
31
+ expect(data).to eq({"key" => "テスト🍺"})
32
+ ensure
33
+ session.ensure_file_closed
34
+ end
35
+ end
36
+ it "should return euc-jp string with internal:euc-jp, exterenal:utf8" do
37
+ option = {
38
+ :internal_encoding => "EUC-JP",
39
+ :external_encoding => "UTF-8",
40
+ :encoding_option => {
41
+ :undef => :replace
42
+ }
43
+ }
44
+ session = PHPSession.new(@session_file[:dir_name], @session_file[:session_id], option)
45
+ begin
46
+ data = session.load
47
+ expect(data).to eq({"key" => "テスト🍺".encode("EUC-JP", {:undef => :replace})})
48
+ ensure
49
+ session.ensure_file_closed
50
+ end
51
+ end
52
+ after do
53
+ File.delete(@session_file[:file_path])
54
+ end
55
+ end
6
56
  context "when session file exists" do
7
57
  before do
8
58
  @session_file = create_dummy_session_file('key|s:1:"a";')
@@ -42,12 +92,19 @@ describe PHPSession do
42
92
  end
43
93
 
44
94
  it "should save session_data in session_file" do
45
- session = PHPSession.new(@session_file[:dir_name], @session_file[:session_id])
95
+ option = {
96
+ :external_encoding => "EUC-JP",
97
+ :internal_encoding => "UTF-8",
98
+ :encoding_option => {:undef => :replace}
99
+ }
100
+ session = PHPSession.new(@session_file[:dir_name], @session_file[:session_id], option)
46
101
  data = session.load
47
- data["key"] = "b"
102
+ data["key"] = "テスト🍣"
48
103
  session.commit(data)
49
104
 
50
- expect(IO.read(@session_file[:file_path])).to eq('key|s:1:"b";')
105
+ # read in bytesequence mode to avoid encoding conversion
106
+ byte_sequence = IO.read(@session_file[:file_path], File.size(@session_file[:file_path]))
107
+ expect(byte_sequence.force_encoding('EUC-JP')).to eq('key|s:7:"テスト?";'.encode("EUC-JP"))
51
108
  end
52
109
 
53
110
  after do
data/spec/spec_helper.rb CHANGED
@@ -12,3 +12,18 @@ def create_dummy_session_file(text)
12
12
 
13
13
  {:file_path => file_path, :dir_name => dirname, :session_id => session_id}
14
14
  end
15
+
16
+ def with_encoding(internal, external)
17
+ default_internal_was = Encoding.default_internal
18
+ default_external_was = Encoding.default_external
19
+
20
+ Encoding.default_external = external
21
+ Encoding.default_internal = internal
22
+
23
+ begin
24
+ yield
25
+ ensure
26
+ Encoding.default_external = default_external_was
27
+ Encoding.default_internal = default_internal_was
28
+ end
29
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: php_session
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shinpei Maruyama
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-24 00:00:00.000000000 Z
11
+ date: 2013-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -99,7 +99,7 @@ rubyforge_project:
99
99
  rubygems_version: 2.0.0
100
100
  signing_key:
101
101
  specification_version: 4
102
- summary: php_session is a php session file reader/writer. Multibyte string and exclusiv
102
+ summary: php_session is a php session file reader/writer. Multibyte string and exclusive
103
103
  control is supported
104
104
  test_files:
105
105
  - spec/php_session/decoder_spec.rb