Skip to content

Commit 5b52e00

Browse files
committed
General improvements
- Update dependencies. - Test with libvips 8.6.3 on Travis. - Use pyvips for the auto-generated docs/enums (fixes #56). - Regenerate docs/enums. - If statement can be merged with parent one.
1 parent 5ce1460 commit 5b52e00

9 files changed

+617
-644
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ env:
1212
global:
1313
- VIPS_VERSION_MAJOR=8
1414
- VIPS_VERSION_MINOR=6
15-
- VIPS_VERSION_MICRO=0
15+
- VIPS_VERSION_MICRO=3
1616
- PATH=$HOME/vips/bin:$PATH
1717
- LD_LIBRARY_PATH=$HOME/vips/lib:$LD_LIBRARY_PATH
1818
- PKG_CONFIG_PATH=$HOME/vips/lib/pkgconfig:$PKG_CONFIG_PATH

composer.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
"require": {
2020
"php": ">=7.0.11",
2121
"ext-vips": ">=0.1.2",
22-
"psr/log": "^1.0.1"
22+
"psr/log": "^1.0.2"
2323
},
2424
"require-dev": {
25-
"phpunit/phpunit": "^6.3",
25+
"phpunit/phpunit": "^6.5",
2626
"phpdocumentor/phpdocumentor" : "^2.9",
27-
"jakub-onderka/php-parallel-lint": "^0.9.2",
27+
"jakub-onderka/php-parallel-lint": "^1.0.0",
2828
"squizlabs/php_codesniffer": "3.*"
2929
},
3030
"autoload": {

examples/generate_phpdoc.py

+331
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
#!/usr/bin/env python
2+
3+
from pyvips import Image, Operation, GValue, Error, \
4+
ffi, values_for_enum, vips_lib, gobject_lib, \
5+
type_map, type_name, type_from_name, nickname_find
6+
7+
# This file generates the phpdoc comments for the magic methods and properties.
8+
# It's in Python, since we use the whole of FFI, not just the
9+
# small bit exposed by php-vips-ext.
10+
11+
# Regenerate docs with something like:
12+
#
13+
# cd src
14+
# python ../examples/generate_phpdoc.py
15+
16+
# this needs pyvips
17+
#
18+
# pip install --user pyvips
19+
20+
# map a Python gtype to PHP argument type names
21+
gtype_to_php_arg = {
22+
GValue.gbool_type: 'bool',
23+
GValue.gint_type: 'integer',
24+
GValue.gdouble_type: 'float',
25+
GValue.gstr_type: 'string',
26+
GValue.refstr_type: 'string',
27+
GValue.genum_type: 'string',
28+
GValue.gflags_type: 'integer',
29+
GValue.gobject_type: 'string',
30+
GValue.image_type: 'Image',
31+
GValue.array_int_type: 'integer[]|integer',
32+
GValue.array_double_type: 'float[]|float',
33+
GValue.array_image_type: 'Image[]|Image',
34+
GValue.blob_type: 'string'
35+
}
36+
37+
# php result type names are different, annoyingly, and very restricted
38+
gtype_to_php_result = {
39+
GValue.gbool_type: 'bool',
40+
GValue.gint_type: 'integer',
41+
GValue.gdouble_type: 'float',
42+
GValue.gstr_type: 'string',
43+
GValue.refstr_type: 'string',
44+
GValue.genum_type: 'string',
45+
GValue.gflags_type: 'integer',
46+
GValue.gobject_type: 'string',
47+
GValue.image_type: 'Image',
48+
GValue.array_int_type: 'array',
49+
GValue.array_double_type: 'array',
50+
GValue.array_image_type: 'array',
51+
GValue.blob_type: 'string'
52+
}
53+
54+
# values for VipsArgumentFlags
55+
_REQUIRED = 1
56+
_INPUT = 16
57+
_OUTPUT = 32
58+
_DEPRECATED = 64
59+
_MODIFY = 128
60+
61+
# for VipsOperationFlags
62+
_OPERATION_DEPRECATED = 8
63+
64+
65+
def gtype_to_php(gtype, result=False):
66+
"""Map a gtype to PHP type name we use to represent it.
67+
"""
68+
69+
fundamental = gobject_lib.g_type_fundamental(gtype)
70+
71+
gtype_map = gtype_to_php_result if result else gtype_to_php_arg
72+
73+
if gtype in gtype_map:
74+
return gtype_map[gtype]
75+
if fundamental in gtype_map:
76+
return gtype_map[fundamental]
77+
return '<unknown type>'
78+
79+
80+
def remove_prefix(enum_str):
81+
prefix = 'Vips'
82+
83+
if enum_str.startswith(prefix):
84+
return enum_str[len(prefix):]
85+
86+
return enum_str
87+
88+
89+
def generate_operation(operation_name):
90+
op = Operation.new_from_name(operation_name)
91+
92+
# we are only interested in non-deprecated args
93+
args = [[name, flags] for name, flags in op.get_args()
94+
if not flags & _DEPRECATED]
95+
96+
# find the first required input image arg, if any ... that will be self
97+
member_x = None
98+
for name, flags in args:
99+
if ((flags & _INPUT) != 0 and
100+
(flags & _REQUIRED) != 0 and
101+
op.get_typeof(name) == GValue.image_type):
102+
member_x = name
103+
break
104+
105+
required_input = [name for name, flags in args
106+
if (flags & _INPUT) != 0 and
107+
(flags & _REQUIRED) != 0 and
108+
name != member_x]
109+
110+
required_output = [name for name, flags in args
111+
if ((flags & _OUTPUT) != 0 and
112+
(flags & _REQUIRED) != 0) or
113+
((flags & _INPUT) != 0 and
114+
(flags & _REQUIRED) != 0 and
115+
(flags & _MODIFY) != 0)]
116+
117+
result = ' * @method '
118+
if member_x is None:
119+
result += 'static '
120+
if len(required_output) == 0:
121+
result += 'void '
122+
elif len(required_output) == 1:
123+
result += '{0} '.format(gtype_to_php(op.get_typeof(required_output[0]), True))
124+
else:
125+
# we generate a Returns: block for this case, see below
126+
result += 'array '
127+
128+
result += '{0}('.format(operation_name)
129+
for name in required_input:
130+
gtype = op.get_typeof(name)
131+
result += '{0} ${1}, '.format(gtype_to_php(gtype), name)
132+
133+
result += 'array $options = []) '
134+
135+
description = op.get_description()
136+
result += description[0].upper() + description[1:] + '.\n'
137+
138+
# find any Enums we've referenced and output @see lines for them
139+
for name in required_output + required_input:
140+
gtype = op.get_typeof(name)
141+
fundamental = gobject_lib.g_type_fundamental(gtype)
142+
143+
if fundamental != GValue.genum_type:
144+
continue
145+
146+
result += ' * @see {0} for possible values for ${1}\n'.format(remove_prefix(type_name(gtype)), name)
147+
148+
if len(required_output) > 1:
149+
result += ' * Return array with: [\n'
150+
for name in required_output:
151+
gtype = op.get_typeof(name)
152+
blurb = op.get_blurb(name)
153+
result += ' * \'{0}\' => @type {1} {2}\n'.format(name, gtype_to_php(gtype),
154+
blurb[0].upper() + blurb[1:])
155+
result += ' * ];\n'
156+
157+
result += ' * @throws Exception\n'
158+
159+
return result
160+
161+
162+
preamble = """<?php
163+
164+
/**
165+
* This file was generated automatically. Do not edit!
166+
*
167+
* PHP version 7
168+
*
169+
* LICENSE:
170+
*
171+
* Copyright (c) 2016 John Cupitt
172+
*
173+
* Permission is hereby granted, free of charge, to any person obtaining
174+
* a copy of this software and associated documentation files (the
175+
* "Software"), to deal in the Software without restriction, including
176+
* without limitation the rights to use, copy, modify, merge, publish,
177+
* distribute, sublicense, and/or sell copies of the Software, and to
178+
* permit persons to whom the Software is furnished to do so, subject to
179+
* the following conditions:
180+
*
181+
* The above copyright notice and this permission notice shall be
182+
* included in all copies or substantial portions of the Software.
183+
*
184+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
185+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
186+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
187+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
188+
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
189+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
190+
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
191+
*
192+
* @category Images
193+
* @package Jcupitt\\Vips
194+
* @author John Cupitt <jcupitt@gmail.com>
195+
* @copyright 2016 John Cupitt
196+
* @license https://opensource.org/licenses/MIT MIT
197+
* @link https://github.com/jcupitt/php-vips
198+
*/
199+
"""
200+
201+
class_header = """ * @category Images
202+
* @package Jcupitt\\Vips
203+
* @author John Cupitt <jcupitt@gmail.com>
204+
* @copyright 2016 John Cupitt
205+
* @license https://opensource.org/licenses/MIT MIT
206+
* @link https://github.com/jcupitt/php-vips
207+
"""
208+
209+
210+
def generate_auto_doc(filename):
211+
all_nicknames = []
212+
213+
def add_nickname(gtype, a, b):
214+
nickname = nickname_find(gtype)
215+
try:
216+
# can fail for abstract types
217+
op = Operation.new_from_name(nickname)
218+
219+
# we are only interested in non-deprecated operations
220+
if (op.get_flags() & _OPERATION_DEPRECATED) == 0:
221+
all_nicknames.append(nickname)
222+
except Error:
223+
pass
224+
225+
type_map(gtype, add_nickname)
226+
227+
return ffi.NULL
228+
229+
type_map(type_from_name('VipsOperation'), add_nickname)
230+
231+
# add 'missing' synonyms by hand
232+
all_nicknames.append('crop')
233+
234+
# make list unique and sort
235+
all_nicknames = list(set(all_nicknames))
236+
all_nicknames.sort()
237+
238+
# these have hand-written methods, don't autodoc them
239+
no_generate = [
240+
'bandjoin',
241+
'bandrank',
242+
'ifthenelse',
243+
'add',
244+
'subtract',
245+
'multiply',
246+
'divide',
247+
'remainder'
248+
]
249+
all_nicknames = [x for x in all_nicknames if x not in no_generate]
250+
251+
print('Generating {0} ...'.format(filename))
252+
253+
with open(filename, 'w') as f:
254+
f.write(preamble)
255+
f.write('\n')
256+
f.write('namespace Jcupitt\\Vips;\n')
257+
f.write('\n')
258+
f.write('/**\n')
259+
f.write(' * Autodocs for the Image class.\n')
260+
f.write(class_header)
261+
f.write(' *\n')
262+
263+
for nickname in all_nicknames:
264+
f.write(generate_operation(nickname))
265+
266+
f.write(' *\n')
267+
268+
# all magic properties
269+
tmp_file = Image.new_temp_file('%s.v')
270+
all_properties = tmp_file.get_fields()
271+
for name in all_properties:
272+
php_name = name.replace('-', '_')
273+
gtype = tmp_file.get_typeof(name)
274+
fundamental = gobject_lib.g_type_fundamental(gtype)
275+
276+
f.write(' * @property {0} ${1} {2}\n'.format(gtype_to_php(gtype), php_name, tmp_file.get_blurb(name)))
277+
278+
if fundamental == GValue.genum_type:
279+
f.write(' * @see {0} for possible values\n'.format(remove_prefix(type_name(gtype))))
280+
281+
f.write(' */\n')
282+
f.write('abstract class ImageAutodoc\n')
283+
f.write('{\n')
284+
f.write('}\n')
285+
286+
287+
def generate_enums():
288+
# otherwise we're missing some enums
289+
vips_lib.vips_token_get_type()
290+
vips_lib.vips_saveable_get_type()
291+
vips_lib.vips_image_type_get_type()
292+
293+
all_enums = []
294+
295+
def add_enum(gtype, a, b):
296+
nickname = type_name(gtype)
297+
all_enums.append(nickname)
298+
299+
type_map(gtype, add_enum)
300+
301+
return ffi.NULL
302+
303+
type_map(type_from_name('GEnum'), add_enum)
304+
305+
for name in all_enums:
306+
gtype = type_from_name(name)
307+
php_name = remove_prefix(name)
308+
309+
print('Generating {0}.php ...'.format(php_name))
310+
311+
with open('{0}.php'.format(php_name), 'w') as f:
312+
f.write(preamble)
313+
f.write('\n')
314+
f.write('namespace Jcupitt\\Vips;\n')
315+
f.write('\n')
316+
f.write('/**\n')
317+
f.write(' * The {0} enum.\n'.format(php_name))
318+
f.write(class_header)
319+
f.write(' */\n')
320+
f.write('abstract class {0}\n'.format(php_name))
321+
f.write('{\n')
322+
323+
for value in values_for_enum(gtype):
324+
php_name = value.replace('-', '_').upper()
325+
f.write(' const {0} = \'{1}\';\n'.format(php_name, value))
326+
327+
f.write('}\n')
328+
329+
330+
generate_auto_doc('ImageAutodoc.php')
331+
generate_enums()

0 commit comments

Comments
 (0)