At Net Perspective we created an image editor in flash that we just released for sale called ImagePlane.
While ImagePlane has already been creatively introduced and documented quite thoroughly, I wanted to go over some decisions concerning the PHP demo.
Currently ImagePlane posts saved data to URL via the HTTP POST method. Currently ImagePlane posts the entire Base64 encoded image (after transformations), but when designing the example class I had to account for the fact that I might have to someday support just a list of transformations (for the server to duplicate rather than receiving the entire image again). I also would have to support more than 1 output engine (despite the fact that I myself would just be using GD).
In designing my interface I like to make a quick list of operations and objects I will be expected to perform (no, this isn’t a waterfall document, the list can be broad but I need a general idea of where I’m going):
- Set a filename (and by proximity, output directory and type)
- Select an output engine (either for performance reasons or simply support)
- Constrain image sizes
- Set certain image options (JPEG quality, possibly PNG transparency)
Now since I know I needed to support multiple output engines, I’m guaranteed to use the Adapter pattern. So now we have to split up our tasks among two classes.
Since our Adapter classes will each be very different internally (since they have to emulate and mimic features with different libraries) we’ll want to keep them as small and clean as possible. Stepping through the list above we can see that 3 and 4 are obviously a best fit for our Adapter class since they deal directly with the actual image processing. All 2 really happens to be is a setOutputEngine(Adapter_Interface $engine)
type method, however you can make it fancier by accepting a string for the engine and finding and instantiating the engine manually, but that’s implementation side. Item 1 could be construed as belonging with the image processing but in reality it does not. Setting the output location and type is universal to all libraries, infact it really has nothing to do with image processing.
So we have a nice delegation of tasks to our objects we can start building. First things first, after creating our main shell class we create an interface (since things are so wildly different amongst image processing engines there’s not much we can replicate so an interface will work better here as opposed to inheriting an abstract class) for our Adapter class.
The adapter class is going to need to do a few things. Specifically its going to need to load from a string (base64 encoded), set a few options (preserveTransparency and setOutQuality, for jpegs), and save the image data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
In our primary class we are probably best splitting up the outfile into 3 separate segments. We should force good filenames by making the user put in the output directory, output name, THEN output type. This way we can easily fix our filenames, check directory problems, and enforce output type (and proper extensions) without having to hack through pathinfo
results only to find the user specified some weird extension for a type that we didn’t account for. Plus this leaves us open to in the future specifying multiple output files without having to include directories on all them, or specifying multiple output types again without having to retype the entire filename.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
|
You can see that on my set* functions I return the $this
variable. This is called the fluent interface, and it allows you to make calls like $object->method()->method()->method();
which makes for compact, and in some cases, semantic code.
You can see that spending just a little bit of time we now have an easily alterable utility class to process uploads from ImagePlane with a very low footprint for the actual end user:
1 2 3 4 5 6 7 8 9 10 |
|
Now obviously this literal code snippet presents security issues but they can be tackled. Firstly the above code snippet can easily be moved inside an if block checking for authentication credentials (like an $acl->isLoggedIn()
or similar), as well as checking for tokens via the otherData
input variable as described in the documentation.
Stay tuned, hopefully I’ll be posting more in the future on OO design and provide more complex design topics in the future.
In the mean time if you want an excellent, classic book, I suggest you grab a copy of Design Patterns: Elements of Reusable Object-Oriented Software by The Gang of Four.