Completed
Push — new-committers ( 29cb6f...bcba16 )
by Sam
12:18 queued 33s
created

Money::getAllowedCurrencies()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * Partially based on Zend_Currency.
4
 *
5
 * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
6
 * @license   http://framework.zend.com/license/new-bsd	 New BSD License
7
 * @version   $Id: Currency.php 6137 2007-08-19 14:55:27Z shreef $
8
 */
9
10
require_once 'Zend/Currency.php';
11
12
/**
13
 * Implements the "Money" pattern.
14
 *
15
 * @see http://www.martinfowler.com/eaaCatalog/money.html
16
 *
17
 * @todo Support different ways of rounding
18
 * @todo Equality operators
19
 * @todo Addition, substraction and allocation of values
20
 * @todo Model validation for $allowedCurrencies
21
 *
22
 * @package framework
23
 * @subpackage model
24
 */
25
class Money extends DBField implements CompositeDBField {
26
27
	/**
28
	 * @var string $getCurrency()
29
	 */
30
	protected $currency;
31
32
	/**
33
	 * @var float $currencyAmount
34
	 */
35
	protected $amount;
36
37
	/**
38
	 * @var boolean $isChanged
39
	 */
40
	protected $isChanged = false;
41
42
	/**
43
	 * @var string $locale
44
	 */
45
	protected $locale = null;
46
47
	/**
48
	 * @var Zend_Currency
49
	 */
50
	protected $currencyLib;
51
52
	/**
53
	 * Limit the currencies
54
	 * @var array $allowedCurrencies
55
	 */
56
	protected $allowedCurrencies;
57
58
	/**
59
	 * @param array
60
	 */
61
	private static $composite_db = array(
62
		"Currency" => "Varchar(3)",
63
		"Amount" => 'Decimal(19,4)'
64
	);
65
66
	public function __construct($name = null) {
67
		$this->currencyLib = new Zend_Currency(null, i18n::get_locale());
68
69
		parent::__construct($name);
70
	}
71
72
	public function compositeDatabaseFields() {
73
		return self::$composite_db;
74
	}
75
76
	public function requireField() {
77
		$fields = $this->compositeDatabaseFields();
78
		if($fields) foreach($fields as $name => $type){
0 ignored issues
show
Bug Best Practice introduced by
The expression $fields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
79
			DB::require_field($this->tableName, $this->name.$name, $type);
80
		}
81
	}
82
83
	public function writeToManipulation(&$manipulation) {
84
		if($this->getCurrency()) {
85
			$manipulation['fields'][$this->name.'Currency'] = $this->prepValueForDB($this->getCurrency());
86
		} else {
87
			$manipulation['fields'][$this->name.'Currency']
88
				= DBField::create_field('Varchar', $this->getCurrency())->nullValue();
89
		}
90
91
		if($this->getAmount()) {
92
			$manipulation['fields'][$this->name.'Amount'] = $this->getAmount();
93
		} else {
94
			$manipulation['fields'][$this->name.'Amount']
95
				= DBField::create_field('Decimal', $this->getAmount())->nullValue();
96
		}
97
	}
98
99
	public function addToQuery(&$query) {
100
		parent::addToQuery($query);
0 ignored issues
show
Documentation introduced by
$query is of type object<SQLQuery>, but the function expects a object<SS_Query>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
101
		$query->selectField(sprintf('"%sAmount"', $this->name));
102
		$query->selectField(sprintf('"%sCurrency"', $this->name));
103
	}
104
105
	public function setValue($value, $record = null, $markChanged = true) {
106
		// Convert an object to an array
107
		if($record && $record instanceof DataObject) {
108
			$record = $record->getQueriedDatabaseFields();
109
		}
110
111
		// @todo Allow resetting value to NULL through Money $value field
112
		if ($value instanceof Money && $value->exists()) {
113
			$this->setCurrency($value->getCurrency(), $markChanged);
114
			$this->setAmount($value->getAmount(), $markChanged);
115
			if($markChanged) $this->isChanged = true;
116
		} else if($record && isset($record[$this->name . 'Amount'])) {
117
			if($record[$this->name . 'Amount']) {
118
				if(!empty($record[$this->name . 'Currency'])) {
119
					$this->setCurrency($record[$this->name . 'Currency'], $markChanged);
120
				} else if($currency = (string)$this->config()->default_currency) {
121
					$this->setCurrency($currency, $markChanged);
122
				}
123
124
				$this->setAmount($record[$this->name . 'Amount'], $markChanged);
125
			} else {
126
				$this->value = $this->nullValue();
127
			}
128
			if($markChanged) $this->isChanged = true;
129
		} else if (is_array($value)) {
130
			if (array_key_exists('Currency', $value)) {
131
				$this->setCurrency($value['Currency'], $markChanged);
132
			}
133
			if (array_key_exists('Amount', $value)) {
134
				$this->setAmount($value['Amount'], $markChanged);
135
			}
136
			if($markChanged) $this->isChanged = true;
137
		} else {
138
			// @todo Allow to reset a money value by passing in NULL
139
			//user_error('Invalid value in Money->setValue()', E_USER_ERROR);
140
		}
141
	}
142
143
	/**
144
	 * @return string
145
	 */
146
	public function Nice($options = array()) {
147
		$amount = $this->getAmount();
148
		if(!isset($options['display'])) $options['display'] = Zend_Currency::USE_SYMBOL;
149
		if(!isset($options['currency'])) $options['currency'] = $this->getCurrency();
150
		if(!isset($options['symbol'])) {
151
			$options['symbol'] = $this->currencyLib->getSymbol($this->getCurrency(), $this->getLocale());
152
		}
153
		return (is_numeric($amount)) ? $this->currencyLib->toCurrency($amount, $options) : '';
154
	}
155
156
	/**
157
	 * @return string
158
	 */
159
	public function NiceWithShortname($options = array()){
160
		$options['display'] = Zend_Currency::USE_SHORTNAME;
161
		return $this->Nice($options);
162
	}
163
164
	/**
165
	 * @return string
166
	 */
167
	public function NiceWithName($options = array()){
168
		$options['display'] = Zend_Currency::USE_NAME;
169
		return $this->Nice($options);
170
	}
171
172
	/**
173
	 * @return string
174
	 */
175
	public function getCurrency() {
176
		return $this->currency;
177
	}
178
179
	/**
180
	 * @param string
181
	 */
182
	public function setCurrency($currency, $markChanged = true) {
183
		$this->currency = $currency;
184
		if($markChanged) $this->isChanged = true;
185
	}
186
187
	/**
188
	 * @todo Return casted Float DBField?
189
	 *
190
	 * @return float
191
	 */
192
	public function getAmount() {
193
		return $this->amount;
194
	}
195
196
	/**
197
	 * @param float $amount
198
	 */
199
	public function setAmount($amount, $markChanged = true) {
200
		$this->amount = (float)$amount;
201
		if($markChanged) $this->isChanged = true;
202
	}
203
204
	/**
205
	 * @return boolean
206
	 */
207
	public function exists() {
208
		return ($this->getCurrency() && is_numeric($this->getAmount()));
209
	}
210
211
	/**
212
	 * @return boolean
213
	 */
214
	public function hasAmount() {
215
		$a = $this->getAmount();
216
		return (!empty($a) && is_numeric($a));
217
	}
218
219
	public function isChanged() {
220
		return $this->isChanged;
221
	}
222
223
	/**
224
	 * @param string $locale
225
	 */
226
	public function setLocale($locale) {
227
		$this->locale = $locale;
228
		$this->currencyLib->setLocale($locale);
229
	}
230
231
	/**
232
	 * @return string
233
	 */
234
	public function getLocale() {
235
		return ($this->locale) ? $this->locale : i18n::get_locale();
236
	}
237
238
	/**
239
	 * @return string
240
	 */
241
	public function getSymbol($currency = null, $locale = null) {
242
243
		if($locale === null) $locale = $this->getLocale();
244
		if($currency === null) $currency = $this->getCurrency();
245
246
		return $this->currencyLib->getSymbol($currency, $locale);
247
	}
248
249
	/**
250
	 * @return string
251
	 */
252
	public function getShortName($currency = null, $locale = null) {
253
		if($locale === null) $locale = $this->getLocale();
254
		if($currency === null) $currency = $this->getCurrency();
255
256
		return $this->currencyLib->getShortName($currency, $locale);
257
	}
258
259
	/**
260
	 * @return string
261
	 */
262
	public function getName($currency = null, $locale = null) {
263
		if($locale === null) $locale = $this->getLocale();
264
		if($currency === null) $currency = $this->getCurrency();
265
266
		return $this->currencyLib->getName($currency, $locale);
267
	}
268
269
	/**
270
	 * @param array $arr
271
	 */
272
	public function setAllowedCurrencies($arr) {
273
		$this->allowedCurrencies = $arr;
274
	}
275
276
	/**
277
	 * @return array
278
	 */
279
	public function getAllowedCurrencies() {
280
		return $this->allowedCurrencies;
281
	}
282
283
	/**
284
	 * Returns a CompositeField instance used as a default
285
	 * for form scaffolding.
286
	 *
287
	 * Used by {@link SearchContext}, {@link ModelAdmin}, {@link DataObject::scaffoldFormFields()}
288
	 *
289
	 * @param string $title Optional. Localized title of the generated instance
290
	 * @return FormField
291
	 */
292
	public function scaffoldFormField($title = null) {
293
		$field = new MoneyField($this->name);
294
		$field->setAllowedCurrencies($this->getAllowedCurrencies());
295
		$field->setLocale($this->getLocale());
296
297
		return $field;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $field; (MoneyField) is incompatible with the return type of the parent method DBField::scaffoldFormField of type TextField.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
298
	}
299
300
	/**
301
	 * For backwards compatibility reasons
302
	 * (mainly with ecommerce module),
303
	 * this returns the amount value of the field,
304
	 * rather than a {@link Nice()} formatting.
305
	 */
306
	public function __toString() {
307
		return (string)$this->getAmount();
308
	}
309
}
310