sabre-io / vobject

:date: The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects
http://sabre.io/vobject/
BSD 3-Clause "New" or "Revised" License
569 stars 125 forks source link

Clear way to decode Property params and value #592

Closed ybelenko closed 2 years ago

ybelenko commented 2 years ago

Thanks for useful package! Works like a charm.

It would be great to have a few methods to decode Property parts. In my case I want to store property params as encoded string in database varchar column.

Main goal is to parse VCard property, save it to database then be able to serialize it back to VCard blob without data lost.

name(VARCHAR) params(VARCHAR) value(VARCHAR) value_type(VARCHAR)
EMAIL TYPE=INTERNET,pref johndoe@example.com TEXT

I tried that approach previously:

$input  = "BEGIN:VCARD\r\n{$params}:{$value}\r\nEND:VCARD";
$parser = new MimeDir();
/** @var VCard */
$vCard = $parser->parse($input);

but it doesn't work with unescaped values. So the workaround could be:

$prop = (new VCard())->createProperty('EMAIL', 'johndoe@example.com');
$prop->parameters = Property::decodeParameters('TYPE=INTERNET,pref');
// expected $prop->parameters
// ['TYPE' => ['INTERNET', 'pref']]

It also would be great to have few extra methods in Property class:

class Property
{
    /**
     * @param string $line Serialized full property line 
     * @example            'EMAIL;TYPE=work;PREF=1:jane_doe@email.test'
     * @return Property
     */
    public static function createFromLine(string $line): Property;

    /**
     * @param string $parameters Serialized property parameters
     * @example                  'TYPE=work;PREF=1'
     * @return Parameter[]
     */
    public static function decodeParameters(string $parameters): array;

    /**
     * @param string $value Serialized property value
     * @example             'Long text with unescaped colons, semi, commas and line breaks'
     * @return mixed
     */
    public static function decodeValue(string $value): mixed;
}

of course it's just an example, write methods the way like.

DeepDiver1975 commented 2 years ago

Don't do that - it is highly advised to store the payload as blob and not to split it into parts.

Many clients are having trouble with that.

https://github.com/sabre-io/dav/blob/5c4fb8e1f36cadbd40db7a428866fbb6e34b848c/examples/sql/mysql.addressbooks.sql#L14

ybelenko commented 2 years ago

Don't do that - it is highly advised to store the payload as blob and not to split it into parts.

Many clients are having trouble with that.

https://github.com/sabre-io/dav/blob/5c4fb8e1f36cadbd40db7a428866fbb6e34b848c/examples/sql/mysql.addressbooks.sql#L14

Thanks, for such a fast answer!

By clients do you mean client apps like Apple Contacts, Outlook etc? Just "don't do" is great advise, you should mention it somewhere in the main doc and in the source code. See, if I would build another cloud based contact book then I definitely can keep entire vcards as blobs. But I want to leverage database normalized schema to get best performance at searching and sorting data. There might be many simultaneous updates, beside reads actions. Import and export of VCards is great feature for new app, but not the key one usually.

Could you be more specific about these potential issues? Is there any options, what if store parameters in jCard format? What if I don't use \explode and \implode and try to serialize data EXACTLY like you do in your Property/VCard classes. I've already finished that part of application, tested it with my own medium size vcf file with ~200 contacts, works well so far.

DeepDiver1975 commented 2 years ago

I was looking for the notes about "don't do it" but could not find them. Sorry for that.

With clients I mean any dav client. It basically boils down to the fact some clients expect the vcard to be exactly the same down to the byte comparing up and download.

This can never be achieved by decomposing and composing.

What I would do with respect to searching: extract the properties you are interested for search.

ybelenko commented 2 years ago

With clients I mean any dav client.

Not sure I'm following. DAV clients like the 3party services with some public API, I guess. Which I need to send requests to update stored user data, right? What if I just want to provide vcf files to my app's users so they can manually import them back to mobile device calendar/address book.

What I would do with respect to searching: extract the properties you are interested for search.

Already extracted them and pushed to the database. So in case app user updates any field from his address book(VCards) I need to 1) read original vcard blob, 2) decode it with your package, 3) update that field, 4) encode it back to string and 5) write to the same vcard blob. Am I correct?

Or do you mean I can't modify that vcard string blob at all, maybe I always need to use some external DAV client to edit that blob for me.