├── .gitignore
├── src
├── Offer
│ ├── BookOffer.php
│ ├── VendorModelOffer.php
│ └── Offer.php
├── Exception
│ └── YMLException.php
├── Param.php
├── Storage.php
├── Factory
│ └── Factory.php
├── Category.php
├── Currency.php
├── Parser.php
├── Shop.php
└── Builder.php
├── composer.json
├── README.md
├── phpunit.xml.dist
└── Tests
├── ParserTest.php
└── Fixtures
├── shops.dtd
└── yml.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | vendor/
3 | composer.lock
4 | phpunit.xml
5 |
--------------------------------------------------------------------------------
/src/Offer/BookOffer.php:
--------------------------------------------------------------------------------
1 | =4.0"
13 | },
14 | "autoload": {
15 | "psr-4": { "Sirian\\YMLParser\\": "src/" }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # YandexMarketLanguage Parser #
3 |
4 | ## About ##
5 |
6 | [Yandex Market Language](https://yandex.ru/support/partnermarket/yml/about-yml.xml) parser for PHP.
7 |
8 |
9 | ## 1. Installation ##
10 |
11 | Add the `sirian/yandex-market-language-parser` package to your `require` section in the `composer.json` file.
12 |
13 | ``` bash
14 | $ composer require sirian/yandex-market-language-parser
15 | ```
16 |
17 | ## 2. Usage ##
18 |
19 | ```php
20 |
21 | use Sirian\YMLParser\Parser;
22 |
23 | $parser = new Parser();
24 | $result = $parser->parse($file);
25 |
26 | $shop = $result->getShop();
27 |
28 | foreach ($result->getOffers() as $offer) {
29 | // some logic
30 | }
31 | ```
32 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 | ./Tests/
17 |
18 |
19 |
20 |
21 |
22 | ./src
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/Param.php:
--------------------------------------------------------------------------------
1 | name;
14 | }
15 |
16 | public function setName($name)
17 | {
18 | $this->name = $name;
19 | return $this;
20 | }
21 |
22 | public function getUnit()
23 | {
24 | return $this->unit;
25 | }
26 |
27 | public function setUnit($unit)
28 | {
29 | $this->unit = $unit;
30 | return $this;
31 | }
32 |
33 | public function getValue()
34 | {
35 | return $this->value;
36 | }
37 |
38 | public function setValue($value)
39 | {
40 | $this->value = $value;
41 | return $this;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Offer/VendorModelOffer.php:
--------------------------------------------------------------------------------
1 | vendor;
14 | }
15 |
16 | public function setVendor($vendor)
17 | {
18 | $this->vendor = $vendor;
19 | return $this;
20 | }
21 |
22 | public function getVendorCode()
23 | {
24 | return $this->vendorCode;
25 | }
26 |
27 | public function setVendorCode($vendorCode)
28 | {
29 | $this->vendorCode = $vendorCode;
30 | return $this;
31 | }
32 |
33 | public function getModel()
34 | {
35 | return $this->model;
36 | }
37 |
38 | public function setModel($model)
39 | {
40 | $this->model = $model;
41 | return $this;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Storage.php:
--------------------------------------------------------------------------------
1 | file = tmpfile();
16 | }
17 |
18 | public function addOfferXML($xml)
19 | {
20 | $this->offersCount++;
21 | fputs($this->file, json_encode($xml, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES) . "\n");
22 | }
23 |
24 | public function getShopXML()
25 | {
26 | return $this->shopXML;
27 | }
28 |
29 | public function setShopXML($shopXML)
30 | {
31 | $this->shopXML = $shopXML;
32 |
33 | return $this;
34 | }
35 |
36 | public function getNextOfferXML()
37 | {
38 | fseek($this->file, 0);
39 | while ($line = fgets($this->file)) {
40 | yield $line;
41 | }
42 | }
43 |
44 | public function getOffersCount()
45 | {
46 | return $this->offersCount;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Factory/Factory.php:
--------------------------------------------------------------------------------
1 | id;
29 | }
30 |
31 | public function setId($id)
32 | {
33 | $this->id = $id;
34 | return $this;
35 | }
36 |
37 | public function getParent()
38 | {
39 | return $this->parent;
40 | }
41 |
42 | public function setParent(Category $parent = null)
43 | {
44 | $this->parent = $parent;
45 | return $this;
46 | }
47 |
48 | public function getName()
49 | {
50 | return $this->name;
51 | }
52 |
53 | public function setName($name)
54 | {
55 | $this->name = $name;
56 | return $this;
57 | }
58 |
59 | public function hasParent()
60 | {
61 | return null !== $this->parent;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Tests/ParserTest.php:
--------------------------------------------------------------------------------
1 | parse($file);
22 |
23 | $shop = $result->getShop();
24 |
25 | $xml = simplexml_load_file($file);
26 | $xml = $xml->shop;
27 |
28 | $this->assertCount(3, $shop->getCurrencies());
29 | $this->assertCount(19, $shop->getCategories());
30 | $this->assertEquals((string)$xml->name, $shop->getName());
31 | $this->assertEquals((string)$xml->url, $shop->getUrl());
32 | $this->assertEquals((string)$xml->company, $shop->getCompany());
33 | $this->assertEquals(7, $shop->getOffersCount());
34 |
35 | $offers = iterator_to_array($result->getOffers());
36 |
37 | $this->assertCount(7, $offers);
38 |
39 | $offer = $offers[0];
40 |
41 | $this->assertEquals(3, $offer->getParam('скорость')->getValue());
42 | $this->assertEquals('true', $offer->getAttribute('available'));
43 | $this->assertEquals(25000, $offer->getOldPrice());
44 | $this->assertEquals(15000, $offer->getPrice());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Currency.php:
--------------------------------------------------------------------------------
1 | id;
58 | }
59 |
60 | public function setId($id)
61 | {
62 | $this->id = $id;
63 | return $this;
64 | }
65 |
66 | public function getRate()
67 | {
68 | return $this->rate;
69 | }
70 |
71 | public function setRate($rate)
72 | {
73 | $this->rate = $rate;
74 | return $this;
75 | }
76 |
77 | public function getPlus()
78 | {
79 | return $this->plus;
80 | }
81 |
82 | public function setPlus($plus)
83 | {
84 | $this->plus = $plus;
85 | return $this;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Parser.php:
--------------------------------------------------------------------------------
1 | xmlReader = new \XMLReader();
22 | $this->factory = $factory;
23 | }
24 |
25 | public function parse($file)
26 | {
27 | $this->path = [];
28 |
29 | $this->xmlReader->open($file);
30 | $result = $this->read();
31 | $this->xmlReader->close();
32 |
33 | return $result;
34 | }
35 |
36 | protected function read()
37 | {
38 | $xml = $this->xmlReader;
39 | $shopXML = '';
40 | if (!$this->moveToShop()) {
41 | throw new YMLException('Invalid YML file');
42 | }
43 |
44 | if (!$this->stepIn()) {
45 | throw new YMLException('Invalid YML file');
46 | }
47 |
48 | $storage = new Storage();
49 |
50 | do {
51 | if ('yml_catalog/shop/offers' == $this->getPath()) {
52 | if ($this->stepIn()) {
53 | do {
54 | $storage->addOfferXML($xml->readOuterXml());
55 | } while ($this->moveToNextSibling());
56 | }
57 | } else {
58 | $shopXML .= $xml->readOuterXml();
59 | }
60 | } while ($this->moveToNextSibling());
61 |
62 | $shopXML = '' . $shopXML . '';
63 |
64 | $storage->setShopXML($shopXML);
65 |
66 | return new Builder($this->factory, $storage);
67 | }
68 |
69 |
70 |
71 | private function moveToShop()
72 | {
73 | $xml = $this->xmlReader;
74 |
75 | while ($xml->read()) {
76 | if ($xml->nodeType == \XMLReader::END_ELEMENT) {
77 | array_pop($this->path);
78 | continue;
79 | }
80 |
81 |
82 | if ($xml->nodeType !== \XMLReader::ELEMENT || $xml->isEmptyElement) {
83 | continue;
84 | }
85 |
86 | array_push($this->path, $xml->name);
87 |
88 | if ('yml_catalog/shop' === $this->getPath()) {
89 | return true;
90 | }
91 | }
92 |
93 | return false;
94 | }
95 |
96 | private function getPath()
97 | {
98 | return implode('/', $this->path);
99 | }
100 |
101 | private function stepIn()
102 | {
103 | $xml = $this->xmlReader;
104 | if ($xml->isEmptyElement) {
105 | return false;
106 | }
107 | while ($xml->read()) {
108 | if (\XMLReader::ELEMENT == $xml->nodeType) {
109 | array_push($this->path, $xml->name);
110 | return true;
111 | }
112 | if (\XMLReader::END_ELEMENT == $xml->nodeType) {
113 | array_pop($this->path);
114 | return false;
115 | }
116 | }
117 | return false;
118 | }
119 |
120 | private function moveToNextSibling()
121 | {
122 | $xml = $this->xmlReader;
123 | array_pop($this->path);
124 | while ($xml->next()) {
125 | if (\XMLReader::ELEMENT == $xml->nodeType) {
126 | array_push($this->path, $xml->name);
127 | return true;
128 | }
129 |
130 | if (\XMLReader::END_ELEMENT == $xml->nodeType) {
131 | return false;
132 | }
133 | }
134 | return false;
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/Shop.php:
--------------------------------------------------------------------------------
1 | name;
84 | }
85 |
86 | public function setName($name)
87 | {
88 | $this->name = $name;
89 | return $this;
90 | }
91 |
92 | public function getCompany()
93 | {
94 | return $this->company;
95 | }
96 |
97 | public function setCompany($company)
98 | {
99 | $this->company = $company;
100 | return $this;
101 | }
102 |
103 | public function getUrl()
104 | {
105 | return $this->url;
106 | }
107 |
108 | public function setUrl($url)
109 | {
110 | $this->url = $url;
111 | return $this;
112 | }
113 |
114 | public function getPlatform()
115 | {
116 | return $this->platform;
117 | }
118 |
119 | public function setPlatform($platform)
120 | {
121 | $this->platform = $platform;
122 | return $this;
123 | }
124 |
125 | public function getVersion()
126 | {
127 | return $this->version;
128 | }
129 |
130 | public function setVersion($version)
131 | {
132 | $this->version = $version;
133 | return $this;
134 | }
135 |
136 | public function getAgency()
137 | {
138 | return $this->agency;
139 | }
140 |
141 | public function setAgency($agency)
142 | {
143 | $this->agency = $agency;
144 | return $this;
145 | }
146 |
147 | public function getEmail()
148 | {
149 | return $this->email;
150 | }
151 |
152 | public function setEmail($email)
153 | {
154 | $this->email = $email;
155 | return $this;
156 | }
157 |
158 | public function addCurrency(Currency $currency)
159 | {
160 | $this->currencies[$currency->getId()] = $currency;
161 | return $this;
162 | }
163 |
164 | public function addCategory(Category $category)
165 | {
166 | $this->categories[$category->getId()] = $category;
167 | return $this;
168 | }
169 |
170 | public function getCategory($id)
171 | {
172 | return isset($this->categories[$id]) ? $this->categories[$id] : null;
173 | }
174 |
175 | public function getCategories()
176 | {
177 | return $this->categories;
178 | }
179 |
180 | public function getCurrency($id)
181 | {
182 | return isset($this->currencies[$id]) ? $this->currencies[$id] : null;
183 | }
184 |
185 | public function getCurrencies()
186 | {
187 | return $this->currencies;
188 | }
189 |
190 | public function getOffersCount()
191 | {
192 | return $this->offersCount;
193 | }
194 |
195 | public function setOffersCount($offersCount)
196 | {
197 | $this->offersCount = $offersCount;
198 |
199 | return $this;
200 | }
201 |
202 | public function getXml()
203 | {
204 | return $this->xml;
205 | }
206 |
207 | public function setXml($xml)
208 | {
209 | $this->xml = $xml;
210 |
211 | return $this;
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/Tests/Fixtures/shops.dtd:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
24 |
25 |
26 |
43 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
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 |
136 |
137 |
138 |
139 |
140 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/src/Builder.php:
--------------------------------------------------------------------------------
1 | factory = $factory;
28 | $this->storage = $storage;
29 | }
30 |
31 | public function getShop()
32 | {
33 | if (null == $this->shop) {
34 | $this->buildShop();
35 | }
36 | return $this->shop;
37 | }
38 |
39 | /**
40 | * @return Offer[]
41 | */
42 | public function getOffers()
43 | {
44 | $count = 0;
45 | foreach ($this->storage->getNextOfferXML() as $xml) {
46 | $xml = simplexml_load_string(json_decode($xml));
47 | $offer = $this->buildOffer($xml);
48 | yield $count => $offer;
49 | $count++;
50 | }
51 | }
52 |
53 | protected function buildShop()
54 | {
55 | $this->shop = $this->factory->createShop();
56 | $xml = simplexml_load_string($this->storage->getShopXML());
57 |
58 | foreach ($xml->xpath('//currencies/currency') as $elem) {
59 | $this->shop->addCurrency($this->buildCurrency($elem));
60 | }
61 |
62 | $this->buildCategories($xml);
63 |
64 | $this
65 | ->shop
66 | ->setName((string)$xml->name)
67 | ->setAgency((string)$xml->agency)
68 | ->setEmail((string)$xml->email)
69 | ->setCompany((string)$xml->company)
70 | ->setPlatform((string)$xml->platform)
71 | ->setUrl((string)$xml->url)
72 | ->setVersion((string)$xml->version)
73 | ->setOffersCount($this->storage->getOffersCount())
74 | ->setXml($xml)
75 | ;
76 | }
77 |
78 | protected function createParam(\SimpleXMLElement $xml)
79 | {
80 | $name = (string)$xml['name'];
81 | $unit = (string)$xml['unit'];
82 |
83 | $param = $this->factory->createParam();
84 |
85 | $param
86 | ->setName($name)
87 | ->setUnit($unit)
88 | ->setValue((string)$xml)
89 | ;
90 |
91 | return $param;
92 | }
93 |
94 | protected function buildCategories(\SimpleXMLElement $xml)
95 | {
96 | $parents = [];
97 | foreach ($xml->xpath('//categories/category') as $xml) {
98 | $this->getShop()->addCategory($this->buildCategory($xml));
99 |
100 | foreach (['parentId', 'parent_id'] as $field) {
101 | if (isset($xml[$field])) {
102 | $parents[(string)$xml['id']] = (string)$xml[$field];
103 | break;
104 | }
105 | }
106 | }
107 |
108 | foreach ($parents as $id => $parentId) {
109 | if ($id != $parentId) {
110 | $parent = $this->getShop()->getCategory($parentId);
111 | } else {
112 | $parent = null;
113 | }
114 | $this
115 | ->getShop()
116 | ->getCategory($id)
117 | ->setParent($parent)
118 | ;
119 | }
120 | }
121 |
122 | protected function buildCurrency(\SimpleXMLElement $xml)
123 | {
124 | $id = Currency::normalize((string)$xml['id']);
125 |
126 | $currency = $this->factory->createCurrency();
127 | $currency
128 | ->setId($id)
129 | ->setRate((string)$xml['rate'])
130 | ->setPlus((int)$xml['plus'])
131 | ;
132 |
133 | return $currency;
134 | }
135 |
136 | protected function buildCategory(\SimpleXMLElement $xml)
137 | {
138 | $id = (string)$xml['id'];
139 |
140 | $parents[$id] = (string)$xml['parentId'];
141 |
142 | $category = $this->factory->createCategory();
143 |
144 | $category
145 | ->setId($id)
146 | ->setName((string)$xml)
147 | ;
148 |
149 | return $category;
150 | }
151 |
152 | protected function buildOffer(\SimpleXMLElement $xml)
153 | {
154 | $type = (string)$xml['type'];
155 |
156 | if (!$type) {
157 | $type = 'vendor.model';
158 | }
159 |
160 | $offer = $this->factory->createOffer($type);
161 | foreach ($xml->attributes() as $key => $value) {
162 | $offer->setAttribute($key, (string)$value);
163 | }
164 |
165 | $offer
166 | ->setId((string)$xml['id'])
167 | ->setAvailable(((string)$xml['available']) == 'true' ? true : false)
168 | ->setType($type)
169 | ->setXml($xml)
170 | ;
171 |
172 | foreach ($xml->param as $param) {
173 | $offer->addParam($this->createParam($param));
174 | }
175 |
176 | foreach ($xml as $field => $value) {
177 | foreach (['add', 'set'] as $method) {
178 | $method .= $this->camelize($field);
179 | if (!in_array($field, ['param']) && method_exists($offer, $method)) {
180 | call_user_func([$offer, $method], count($value->children()) ? $value : (string)$value);
181 | break;
182 | }
183 | }
184 | }
185 |
186 | $currencyId = Currency::normalize((string)$xml->currencyId);
187 |
188 | if ($this->getShop()->getCurrency($currencyId)) {
189 | $offer->setCurrency($this->getShop()->getCurrency($currencyId));
190 | }
191 |
192 | $categoryId = (string)$xml->categoryId;
193 |
194 | if ($this->getShop()->getCategory($categoryId)) {
195 | $offer->setCategory($this->getShop()->getCategory($categoryId));
196 | }
197 | return $offer;
198 | }
199 |
200 | private function camelize($field)
201 | {
202 | return strtr(ucwords(strtr($field, array('_' => ' ', '.' => '_ '))), array(' ' => ''));
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/src/Offer/Offer.php:
--------------------------------------------------------------------------------
1 | id;
74 | }
75 |
76 | public function setId($id)
77 | {
78 | $this->id = $id;
79 | return $this;
80 | }
81 |
82 | public function getType()
83 | {
84 | return $this->type;
85 | }
86 |
87 | public function setType($type)
88 | {
89 | $this->type = (string)$type;
90 | return $this;
91 | }
92 |
93 | public function getName()
94 | {
95 | return $this->name;
96 | }
97 |
98 | public function setName($name)
99 | {
100 | $this->name = (string)$name;
101 | return $this;
102 | }
103 |
104 | public function getDescription()
105 | {
106 | return $this->description;
107 | }
108 |
109 | public function setDescription($description)
110 | {
111 | $this->description = (string)$description;
112 | return $this;
113 | }
114 |
115 | public function isAvailable()
116 | {
117 | return $this->available;
118 | }
119 |
120 | public function setAvailable($available)
121 | {
122 | $this->available = (bool)$available;
123 | return $this;
124 | }
125 |
126 | public function getUrl()
127 | {
128 | return $this->url;
129 | }
130 |
131 | public function setUrl($url)
132 | {
133 | $this->url = (string)$url;
134 | return $this;
135 | }
136 |
137 | public function getPrice()
138 | {
139 | return $this->price;
140 | }
141 |
142 | public function setPrice($price)
143 | {
144 | $this->price = (float)$price;
145 | return $this;
146 | }
147 |
148 | public function getCurrency()
149 | {
150 | return $this->currency;
151 | }
152 |
153 | public function setCurrency($currency)
154 | {
155 | $this->currency = $currency;
156 | return $this;
157 | }
158 |
159 | public function getCategory()
160 | {
161 | return $this->category;
162 | }
163 |
164 | public function setCategory($category)
165 | {
166 | $this->category = $category;
167 | return $this;
168 | }
169 |
170 | public function getShop()
171 | {
172 | return $this->shop;
173 | }
174 |
175 | public function setShop($shop)
176 | {
177 | $this->shop = $shop;
178 | return $this;
179 | }
180 |
181 | public function getPictures()
182 | {
183 | return $this->pictures;
184 | }
185 |
186 | public function setPictures($pictures)
187 | {
188 | $this->pictures = [];
189 | if (is_array($pictures) || $pictures instanceof \Traversable) {
190 | foreach ($pictures as $picture) {
191 | $this->addPicture($picture);
192 | }
193 | }
194 |
195 | return $this;
196 | }
197 |
198 | public function addPicture($picture)
199 | {
200 | $this->pictures[] = (string)$picture;
201 | return $this;
202 | }
203 |
204 | public function getTypePrefix()
205 | {
206 | return $this->typePrefix;
207 | }
208 |
209 | public function setTypePrefix($typePrefix)
210 | {
211 | $this->typePrefix = $typePrefix;
212 |
213 | return $this;
214 | }
215 |
216 | public function getAttributes()
217 | {
218 | return $this->attributes;
219 | }
220 |
221 | public function setAttributes($attributes)
222 | {
223 | $this->attributes = $attributes;
224 |
225 | return $this;
226 | }
227 |
228 | public function setAttribute($key, $param)
229 | {
230 | $this->attributes[$key] = $param;
231 | return $this;
232 | }
233 |
234 | public function hasAttribute($key)
235 | {
236 | return isset($this->attributes[$key]);
237 | }
238 |
239 | public function getAttribute($key, $defaultValue = null)
240 | {
241 | return $this->hasAttribute($key) ? $this->attributes[$key] : $defaultValue;
242 | }
243 |
244 | public function getXml()
245 | {
246 | return $this->xml;
247 | }
248 |
249 | public function setXml($xml)
250 | {
251 | $this->xml = $xml;
252 |
253 | return $this;
254 | }
255 |
256 | public function getParam($name)
257 | {
258 | $name = mb_strtolower($name, 'UTF-8');
259 | return isset($this->params[$name]) ? $this->params[$name] : null;
260 | }
261 |
262 |
263 | public function getParams()
264 | {
265 | return $this->params;
266 | }
267 |
268 | public function addParam(Param $param)
269 | {
270 | $name = mb_strtolower($param->getName(), 'UTF-8');
271 | $this->params[$name] = $param;
272 |
273 | return $this;
274 | }
275 |
276 | public function setParams($params)
277 | {
278 | $this->params = [];
279 | if (is_array($params) || $params instanceof \Traversable) {
280 | foreach ($params as $param) {
281 | $this->addParam($param);
282 | }
283 | }
284 |
285 | return $this;
286 | }
287 |
288 | public function getOldPrice()
289 | {
290 | return $this->oldPrice;
291 | }
292 |
293 | public function setOldPrice($oldPrice)
294 | {
295 | $this->oldPrice = $oldPrice;
296 |
297 | return $this;
298 | }
299 | }
300 |
--------------------------------------------------------------------------------
/Tests/Fixtures/yml.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Magazin
6 | Magazin
7 | http://www.magazin.ru/
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 | http://magazin.ru/product_page.asp?pid=14344
45 | 15000
46 | 25000
47 | RUR
48 | 101
49 | http://magazin.ru/img/device14344.jpg
50 | true
51 | 300
52 | Принтер
53 | НP
54 | Q7533A
55 | Color LaserJet 3000
56 | A4, 64Mb, 600x600 dpi, USB 2.0, 29стр/мин ч/б / 15стр/мин цв, лотки на 100л и 250л, плотность до 175г/м, до 60000 стр/месяц
57 | true
58 | Япония
59 | 100
60 | 3
61 |
62 |
63 |
64 | http://magazin.ru/product_page.asp?pid=14345
65 | 100
66 | RUR
67 | 13
68 | http://magazin.ru/img/book14345.jpg
69 | true
70 | 300
71 | Александра Маринина
72 | Все не так. В 2 томах. Том 1
73 | ЭКСМО - Пресс
74 | А. Маринина - королева детектива
75 | 2009
76 | 978-5-699-23647-3
77 | 2
78 | 1
79 | rus
80 | 70x90/32
81 | 288
82 | Все прекрасно в большом патриархальном семействе Руденко. Но — увы! — впечатление это обманчиво: каждого из многочисленных представителей семьи обуревают свои потаенные страсти и запретные желания.
83 | false
84 |
85 |
86 |
87 | http://magazin.ru/product_page.asp?pid=14346
88 | 200
89 | RUR
90 | 14
91 | http://magazin.ru/img/book14346.jpg
92 | Владимир Кунин
93 | Иваnов и Rабинович, или Аj'гоу ту 'Хаjфа!
94 | 1С-Паблишинг, Союз
95 | 2009
96 | 978-5-9677-0757-5
97 | ru
98 | Николай Фоменко
99 | начитана
100 | CD
101 | mp3
102 | 45m23s
103 | Перу Владимира Кунина принадлежат десятки сценариев к кинофильмам, серия книг про КЫСЮ и многое, многое другое.
104 | true
105 |
106 |
107 |
108 | http://magazin.ru/product_page.asp?pid=14347
109 | 450
110 | RUR
111 | 17
112 | http://magazin.ru/img/cd14347.jpg
113 | true
114 | Pink Floyd
115 | Dark Side Of The Moon, Platinum Disc
116 | 1999
117 | CD
118 | Dark Side Of The Moon, поставивший мир на уши невиданным сочетанием звуков, — это всего-навсего девять треков, и даже не все они писались специально для альбома. Порывшись по сусекам, участники Pink Floyd мудро сделали новое из хорошо забытого старого — песен, которые почему-либо не пошли в дело или остались незаконченными. Одним из источников вдохновения стали саундтреки для кинофильмов, которые группа производила в больших количествах.
119 |
120 |
121 |
122 | http://magazin.ru/product_page.asp?pid=14348
123 | 400
124 | RUR
125 | 18
126 | http://magazin.ru/img/dvd14348.jpg
127 | true
128 | Свадьба Мюриэл
129 | 1999
130 | DVD
131 | Тони Колетт (Toni Collette), Рэйчел Грифитс (Rachel Griffiths)
132 | П Дж Хоген
133 | Muriel's wedding
134 | Австралия
135 | Гадкий утенок из провинциального городка покидает свое гнездо, и в компании своей подруги отправляется искать веселой жизни в большой и загадочный город. Фильм о мечтах и реальности, дружбе и юности молодой девушки, приключения которой повторяют судьбы Золушки и героини Джулии Робертс из ставшего классикой фильма Красотка...
136 |
137 |
138 |
139 | http://magazin.ru/product_page.asp?pid=14349
140 | 30000
141 | RUR
142 | 19
143 | http://magazin.ru/img/travel14349.jpg
144 | false
145 | 300
146 | Африка
147 | Египет
148 | Хургада
149 | 7
150 | 01/06/2014
151 | 08/06/2014
152 | Hilton
153 | 5*****
154 | SNG
155 | ALL
156 | авиаперелет, трансфер, проживание, питание, страховка
157 | Авиа
158 | Отдых в Египте.
159 |
160 |
161 |
162 | http://magazin.ru/product_page.asp?pid=14350
163 | 5000
164 | RUR
165 | 6
166 | http://magazin.ru/img/tickets14350.jpg
167 | true
168 | 300
169 | Дмитрий Хворостовский и Национальный филармонический оркестр России. Дирижер — Владимир Спиваков.
170 | Московский международный Дом музыки
171 | Большой зал
172 | Партер р. 1-5
173 | 2014-12-31T19:00
174 | 0
175 | 0
176 | Концерт Дмитрия Хворостовского и Национального филармонического оркестра России под управлением Владимира Спивакова.
177 |
178 |
179 |
180 |
181 |
182 |
--------------------------------------------------------------------------------