Use ConfigParser to read an array from ini file [duplicate] - php

This question already has answers here:
How to ConfigParse a file keeping multiple values for identical keys?
(5 answers)
Closed 9 years ago.
I have read this post, and defined an array in subscriber.ini
[smtp]
subscriber[] = aaa#hotmail.com
subscriber[] = bbb#XX.webmail
subscriber[] = ccc#test.org
Then I try to use ConfigParser to read the array
#!/usr/bin/python
import ConfigParser
CONFIG_FILE = 'subscriber.ini'
config = ConfigParser.ConfigParser()
config.read( CONFIG_FILE )
subscriber = config.get('smtp' , 'subscriber[]' )
print subscriber
It will output the last element, ccc#test.org. But I expect a full subscriber list.
How do I get the array from ini file ?

Python ConfigParser doesn't provide this feature. Use following instead:
[smtp]
subscriber = aaa#hotmail.com bbb#XX.webmail ccc#test.org
Then in your script:
subscriber = config.get('smtp' , 'subscriber').split()

This syntax, where subscriber[] automatically makes subscriber into a list of multiple values, is not a feature of .ini files in general, nor of ConfigParser; it's a feature of Zend_Config_Ini.
In Python, a ConfigParser ini file creates a dict mapping each key to its value. If you have more than one value, it will just override previous values. The magic [] suffix means nothing.
However, the ConfigParser constructor lets you specify a custom dictionary type or factory, in place of the default OrderedDict.
One simple solution would be to use a defaultdict(list) (or an OrderedDefaultDict, which there are recipes for in the docs) for the underlying storage, have __setitem__(self, key, value) do self.dd[key].append(value), and delegate everything else normally. (Or, if you prefer, inherit from defaultdict, override the constructor to pass list to the super, and then just don't override anything but __setitem__.) That will make all of your values into lists.
You could even do something hacky where a value that's only seen once is a single value, but if you see the same name again it becomes a list. I think that would be a terrible idea (do you really want to check the type of config.get('smtp', 'subscriber[]') to decide whether or not you want to iterate over it?), but if you want to, How to ConfigParse a file keeping multiple values for identical keys? shows how.
However, it's not at all hard to reproduce the exact magic you're looking for, where all keys ending in [] are lists (whether they appear once or multiple times), and everything else works like normal (keeps only the last value if it appears multiple times). Something like this:
class MultiDict(collections.OrderedDict):
def __setitem__(self, key, value):
if key.endswith('[]'):
super(MultiDict, self).setdefault(key, []).append(value)
else:
super(MultiDict, self).__setitem__(key, value)
This obviously won't provide all of the extended features that Zend_Config_Ini adds on top of normal .ini files. For example, [group : subgroup : subsub] won't have any special meaning as a group name, nor will key.subkey.subsub as a key name. PHP values TRUE, FALSE, yes, no, and NULL won't get converted to Python values True, False, True, False, and None. Numbers won't magically become numbers. (Actually, this isn't a feature of Zend_Config_Ini, but a misfeature of PHP's leaky typing.) You have to use # comments, rather than freely mixing #, ;, and //. And so on. Any of those features that you want to add, you'll have to add manually, just as you did this one.
As I suggested in a comment, if you really want to have more than two levels of hierarchy, you may be better off with a naturally infinitely-hierarchical format, where any value can be a list or dict of other values.
JSON is ubiquitous nowadays. It may not be quite as human-editable as INI, but I think more people are familiar with it than INI in 2014. And it has the huge advantage that it's a standardized format, and that both Python (2.6+) and PHP (5.2+) come with parsers and pretty-printers for in their standard libraries.
YAML is a more flexible and human-editable format. But you will need third-party modules in both languages (see the list at the YAML site). And it can also bring in some security concerns if you're not careful. (See safe_load and friends in the PyYAML docs; most other libraries have similar features.)

I wonder if you have considered using Michael Foord's configobj module? It seems to be capable
of doing what you want, which might be better that trying to pervert ConfigParser to do what you apparently need.

Related

How can I write this in Python?

$class->categories[$cat->category_parent_id][]=$cat;
I have developed a script where I want to write this php code in python.
How can I create a this categories array in python?
So far I have done this:
categories_c.insert(row["category_parent_id"], row)
But I am not sure if this is the correct implementation.
Let's deconstruct:
$class->categories
This accesses the categories property of the $class object. In Python that's:
klass.categories # (class is a reserved keyword, using klass instead)
We'll assume that categories is a dict here, because you want to do this:
categories[$cat->category_parent_id]
This accesses a particular key of an array, in Python that would be the key of a dict:
categories[cat.category_parent_id]
Now the tricky part:
...[] = $cat
This pushes $cat into the array. In Python that means appending a value to a list. PHP will implicitly create new sub-arrays as necessary if they don't exist. Python doesn't. This is the only part that needs a bit of additional consideration. What you need to know is whether categories[cat.category_parent_id] already exists in your dict or whether you want to create it in the process.
If you know it exists, if categories is a dict of lists in which all cat.category_parent_id keys are already pre-populated, then it's as simple as this:
klass.categories[cat.category_parent_id].append(cat)
However, if the keys don't exist, you must ensure they're created and that their value is set to a list the first time they're accessed. The most compact notation for that in Python is the dict.setdefault method:
klass.categories.setdefault(cat.category_parent_id, []).append(cat)
If categories[cat.category_parent_id] exists, it is returned and you can append to it. If it doesn't exist, it is created and initialised to [] and then returned.

How a hash or mapping works in PHP

In the language of Perl, I define a hash as a mapping between one thing and another or an essential list of elements. As stated in the documentation..
A hash is a basic data type. It uses keys to access its contents.
So basically a hash is close to an array. Their initializations even look very similar.
If I were to create a mapping in Perl, I could do something like below for comparing.
my %map = (
A => [qw(a b c d)],
B => [qw(c d f a)],
C => [qw(b d a e)],
);
my #keys = keys %map;
my %matches;
for my $k ( 1 .. #keys ) {
$matches{$_} |= 2**$k for #{$map{ $keys[$k-1] }};
}
for ( sort keys %matches ) {
my #found;
for my $k ( 1 .. #keys ) {
push #found, $keys[$k-1] if $matches{$_} & 2**$k;
}
print "$_ found in ", (#found? join(',', #found) : 0 ), "\n";
}
Output:
a found in A,C,B
b found in A,C
c found in A,B
d found in A,C,B
e found in C
f found in B
I would like to find out the best method of doing this for performance and efficiency in php
If I understand correctly, you are looking to apply your knowledge of Perl hashes to PHP. If I'm correct, then...
In PHP a "Perl hash" is generally called an "associative array", and PHP implements this as an array that happens to have keys as indexes and its values are just like a regular array. Check out the PHP Array docs for lots of examples about how PHP lets you work with arrays of this (and other) types.
The nice thing about PHP is it is very flexible as to how you can deal with arrays. You can define an array as having key-value pairs then treat it like a regular array and ignore the keys, and that works just fine. You can mix and match...it doesn't complain much.
Philosophically, a hash or map is just a way to keep discrete pieces of related information together. That's all most non-primitive data structures are, and PHP is not very opinionated about how you go about things; it has lots of built-in optimizations, and does a pretty solid job of doing these types of things efficiently.
To answer your questions related to your example:
1) As for simplicity (I think you mean) and maintainability, I don't think there's anything wrong with your use of an associative array. If a data set is in pairs, then key-value pairs is a natural way to express this type of data.
2) As for most efficient, as far as lines of code and script execution overhead goes...well, the use of such a mapping is a vanishingly small task for PHP. I don't think any other way of handling it would matter much, PHP can handle it by the thousands without complaint. Now if you could avoid the use of a regular expression, on the other hand...
3) You're using it, really. Don't over think it - in PHP this is just an "array", and that's it. It's a variable that holds an arbitrary amount of elements, and PHP handles multiple-dimensions or associativity pretty darn well. Well enough that it's almost never going to be the cause of any problem you have.
PHP will handle things like hash/maps behind the scenes very logically and efficiently, to the point that part of the whole point of the language is for you not to bother to try to think about such things. If you have relates pieces of data in chunks, use an array; if the pieces of data comes in pairs, use key-value pairs; if it comes by the dozen, use an "array of arrays" (a multidimensional array where some - or all - of it's elements are arrays).
PHP doesn't do anything stupid like create a massive overhead just because you wanted to use key-value pairs, and it has lots of built-in features like foreach $yourArray as $key => $value and the functions you used like array_keys() and array_values(). Feel free to use them - as core features they are generally pretty darn well optimized!
For what you are doing I would rather use sprintf:
$format = 'Hello %s how are you. Hey %s, hi %s!';
printf($format, 'foo', 'bar', 'baz');

PHP - Exploding on character(s) that can NEVER be user-defined... How?

Ok, am trying to find a character or group of characters, or something that can be used that I can explode from, since the text is user-defined, I need to be able to explode from a value that I have that can never be within the text.
How can I do this?
An example of what I'm trying to do...
$value = 'text|0||#fd9||right';
Ok,
text is something that should never change in here.
0, again not changeable
#fd9 is a user-defined string that can be anything that the user inputs...
and right sets the orientation (either left or right).
So, the problem I'm facing is this: How to explode("||", $value) so that if there is a || within the user-defined part... Example:
$value = 'text|0||Just some || text in here||right';
So, if the user places the || in the user-defined part of the string, than this messes this up. How to do this no matter what the user inputs into the string? So that it should return the following array:
array('text|0', 'Just some || text in here', 'right');
Should I be using different character(s) to explode from? If so, what can I use that the user will not be able to input into the string, or how can I check for this, and fix it? I probably shouldn't be using || in this case, but what can I use to fix this?
Also, the value will be coming from a string at first, and than from the database afterwards (once saved).
Any Ideas?
The problem of how to represent arbitrary data types as strings always runs up against exactly the problem you're describing and it has been solved in many ways already. This process is called serialization and there are many serialization formats, anything from PHP's native serialize to JSON to XML. All these formats specify how to present complex data structures as strings, including escaping rules for how to use characters that have a special meaning in the serialization format in the serialized values themselves.
From the comments:
Ok, well, basically, it's straight forward. I already outlined 13 of the other parameters and how they work in Dream Portal located here: http://dream-portal.net/topic_122.0.html so, you can see how they fit in. I'm working on a fieldset parameter that basically uses all of these parameters and than some to include multiple parameters into 1. Anyways, hope that link helps you, for an idea of what an XML file looks like for a module: http://dream-portal.net/topic_98.0.html look at the info.xml section, pay attention to the <param> tag in there, at the bottom, 2 of them.
It seems to me that a more sensible use of XML would make this a lot easier. I haven't read the whole thing in detail, but an XML element like
<param name="test_param" type="select">0:opt1;opt2;opt3</param>
would make much more sense written as
<select name="test_param">
<option default>opt1</option>
<option>opt2</option>
<option>opt3</option>
</select>
Each unique configuration option can have its own unique element namespace with custom sub-elements depending on the type of parameter you need to represent. Then there's no need to invent a custom mini-format for each possible parameter. It also allows you to create a formal XML schema (whether this will do you any good or not is a different topic, but at least you're using XML as it was meant to be used).
You can encode any user input to base64 and then use it with explode or however you wish.
print base64_encode("abcdefghijklmnopqrstuvwxyz1234567890`~!##$%^&*()_+-=[];,./?>:}{<");
serialized arrays are also not a bad idea at all. it's probably better than using a comma separated string and explode. Drupal makes good use of serialized arrays.
take a look at the PHP manual on how to use it:
serialize()
unserialize()
EDIT: New Solution
Is it a guarantee that text doesn't contain || itself?
If it doesn't, you can use substr() in combination with strpos() and strrpos() instead of explode
Here's what I usually do to get around this problem.
1) capture user's text and save it in a var $user_text;
2) run an str_replace() on $user_text to replace the characters you want to split by:
//replace with some random string the user would hopefully never enter
$modified = str_replace('||','{%^#',$user_text);
3) now you can safely explode your text using ||
4) now run an str_replace on each part of the explode, to set it back to the original user entered text
foreach($parts as &$part) {
$part = str_replace('{%^#','||',$part);
}

How to store numeric values in Zend_Config without loosing its type?

I decided to move some application settings from database to file system to be accessible via Zend_Config.
However I run in to one annoying thing, Zend_Config recognise all values as string ignoring data type.
Is there any magic way to parse config file keeping original types like float, integer, string and what would be best storage for it INI, XML, YAML or JSON?
At least not possible with Zend_Config_Xml:
Configuration data read into
Zend_Config_Xml are always returned as
strings. Conversion of data from
strings to other types is left to
developers to suit their particular
needs.
Not sure about the others. You can very much do something like
// config.php
return array('foo' => 1);
// bootstrap
$config = new Zend_Config(include 'config.php');
var_dump($config->foo); // int(1)
Zend will automagically turn integers, floats, and bools into their respective types with JSON and YAML. XML is always string, and INI has all the caveats and power of parse_ini_file() when it comes to choosing type.
If you want an explicit type, such as a particular object, you need to include that in the serialized data.

Best method of passing/return values

The reason I am asking this question is because I have landed my first real (yes, a paid office job - no more volunteering!) Web Development job about two months ago. I have a couple of associates in computer information systems (web development and programming). But as many of you know, what you learn in college and what you need in the job site can be very different and much more. I am definitely learning from my job - I recreated the entire framework we use from scratch in a MVC architecture - first time doing anything related to design patterns.
I was wondering what you would recommend as the best way to pass/return values around in OO PHP? Right now I have not implement any sort of standard, but I would like to create one before the size of the framework increases any more. I return arrays when more than 1 value needs to get return, and sometimes pass arrays or have multiple parameters. Is arrays the best way or is there a more efficient method, such as json? I like the idea of arrays in that to pass more values or less, you just need to change the array and not the function definition itself.
Thank you all, just trying to become a better developer.
EDIT: I'm sorry all, I thought I had accepted an answer for this question. My bad, very, very bad.
How often do you run across a situation where you actually need multiple return values? I can't imagine it's that often.
And I don't mean a scenario where you are returning something that's expected to be an enumerable data collection of some sort (i.e., a query result), but where the returned array has no other meaning that to just hold two-or-more values.
One technique the PHP library itself uses is reference parameter, such as with preg_match(). The function itself returns a single value, a boolean, but optionally uses the supplied 3rd parameter to store the matched data. This is, in essence, a "second return value".
Definitely don't use a data interchange format like JSON. the purpose of these formats is to move data between disparate systems in an expected, parse-able way. In a single PHP execution you don't need that.
You can return anything you want: a single value, an array or a reference (depending on the function needs). Just be consistent.
But please don't use JSON internally. It just produces unnecessary overhead.
I also use arrays for returning multiple values, but in practice it doesn't happen very often. If it does, it's generally a sensible grouping of data, such as returning array('x'=>10,'y'=>10) from a function called getCoordinates(). If you find yourself doing lots of processing and returning wads of data in arrays from a lot of functions, there's probably some refactoring that can be done to put the work into smaller units.
That being said, you mentioned:
I like the idea of arrays in that to pass more values or less, you just need to change the array and not the function definition itself.
In that regard, another technique you might be interested in is using functions with variable numbers of arguments. It is perfectly acceptable to declare a function with no parameters:
function stuff() {
//do some stuff
}
but call it with all the parameters you care to give it:
$x = stuff($var1, $var2, $var3, $var4);
By using func_get_args(), func_get_arg() (singular) and func_num_args() you can easily find/loop all the parameters that were passed. This works very well if you don't have specific parameters in mind, say for instance a sum() function:
function sum()
{
$out = 0;
for($i = 0; $i < $c = func_num_args(); $i++) {
$out += func_get_arg($i);
}
return $out;
}
//echoes 35
echo sum(10,10,15);
Food for thought, maybe you'll find it useful.
The only thing I'm careful to avoid passing/returning arrays where the keys have "special" meaning. Example:
<?php
// Bad. Don't pass around arrays with 'special' keys
$personArray = array("eyeColor"=>"blue", "height"=>198, "weight"=>103, ...);
?>
Code that uses an array like this is harder to refactor and debug. This type of structure is better represented as an object.
<?php
Interface Person {
/**
* #return string Color Name
*/
public function getEyeColor();
...
}
?>
This interface provides a contract that the consuming code can rely on.
Other than that I can't think of any reason to limit yourself.
Note: to be clear, associative arrays are great for list data. like:
<?php
// Good array
$usStates = array("AL"=>"ALABAMA", "AK"="ALASKA", ... );
?>

Categories